forked from mirror/coolify-cli
412 lines
12 KiB
PowerShell
412 lines
12 KiB
PowerShell
# Coolify CLI Installer for Windows
|
|
# This script installs the coolify-cli from GitHub releases
|
|
# Supports Windows on amd64/arm64 architectures
|
|
|
|
#Requires -Version 5.1
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Position = 0)]
|
|
[string]$Version = "",
|
|
|
|
[Parameter()]
|
|
[switch]$User,
|
|
|
|
[Parameter()]
|
|
[switch]$Help,
|
|
|
|
[Parameter()]
|
|
[string]$InstallDir = ""
|
|
)
|
|
|
|
# Support environment variables for web-based installation
|
|
if (-not $Version -and $env:COOLIFY_VERSION) {
|
|
$Version = $env:COOLIFY_VERSION
|
|
}
|
|
if (-not $User.IsPresent -and $env:COOLIFY_USER_INSTALL) {
|
|
$User = [bool]($env:COOLIFY_USER_INSTALL -match '^(1|true|yes)$')
|
|
}
|
|
if (-not $InstallDir -and $env:COOLIFY_INSTALL_DIR) {
|
|
$InstallDir = $env:COOLIFY_INSTALL_DIR
|
|
}
|
|
|
|
# Configuration
|
|
$Script:REPO = "coollabsio/coolify-cli"
|
|
$Script:BINARY_NAME = "coolify.exe"
|
|
$Script:GLOBAL_INSTALL_DIR = "$env:ProgramFiles\Coolify"
|
|
$Script:USER_INSTALL_DIR = "$env:LOCALAPPDATA\Coolify"
|
|
$Script:TEMP_FILE = ""
|
|
|
|
# Error action preference
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# Cleanup function
|
|
function Cleanup {
|
|
if ($Script:TEMP_FILE -and (Test-Path $Script:TEMP_FILE)) {
|
|
Remove-Item -Path $Script:TEMP_FILE -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
# Register cleanup on exit
|
|
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Cleanup } | Out-Null
|
|
|
|
# Show help
|
|
function Show-Help {
|
|
Write-Host @"
|
|
Coolify CLI Installer for Windows
|
|
|
|
Usage: .\install.ps1 [OPTIONS] [VERSION]
|
|
|
|
OPTIONS:
|
|
-User Install to user directory (no admin rights required)
|
|
-InstallDir Custom installation directory
|
|
-Help Show this help message
|
|
|
|
ARGUMENTS:
|
|
VERSION Specific version to install (e.g., v1.0.0)
|
|
If not specified, installs the latest release
|
|
|
|
EXAMPLES:
|
|
.\install.ps1 Install latest version to Program Files
|
|
.\install.ps1 -User Install latest version to user directory
|
|
.\install.ps1 v1.0.0 Install specific version to Program Files
|
|
.\install.ps1 -User v1.0.0 Install specific version to user directory
|
|
.\install.ps1 -InstallDir "C:\Tools" Install to custom directory
|
|
|
|
NOTES:
|
|
- Administrator privileges are required for global installation
|
|
- User installation does not require admin rights
|
|
- The installation directory will be added to PATH if not already present
|
|
|
|
"@
|
|
exit 0
|
|
}
|
|
|
|
# Write colored output
|
|
function Write-ColorOutput {
|
|
param(
|
|
[string]$Message,
|
|
[string]$ForegroundColor = "White"
|
|
)
|
|
Write-Host $Message -ForegroundColor $ForegroundColor
|
|
}
|
|
|
|
function Write-Success {
|
|
param([string]$Message)
|
|
Write-ColorOutput $Message -ForegroundColor Green
|
|
}
|
|
|
|
function Write-Warning {
|
|
param([string]$Message)
|
|
Write-ColorOutput $Message -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-ErrorMessage {
|
|
param([string]$Message)
|
|
Write-ColorOutput $Message -ForegroundColor Red
|
|
}
|
|
|
|
# Error handler
|
|
function Stop-WithError {
|
|
param([string]$Message)
|
|
Write-ErrorMessage "Error: $Message"
|
|
Cleanup
|
|
throw $Message
|
|
}
|
|
|
|
# Check if running as administrator
|
|
function Test-Administrator {
|
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
}
|
|
|
|
# Detect platform and architecture
|
|
function Get-PlatformInfo {
|
|
$os = "windows"
|
|
|
|
# Detect architecture
|
|
$arch = switch ($env:PROCESSOR_ARCHITECTURE) {
|
|
"AMD64" { "amd64" }
|
|
"ARM64" { "arm64" }
|
|
"x86" { Stop-WithError "32-bit Windows is not supported" }
|
|
default { Stop-WithError "Unsupported architecture: $env:PROCESSOR_ARCHITECTURE" }
|
|
}
|
|
|
|
return @{
|
|
OS = $os
|
|
Arch = $arch
|
|
}
|
|
}
|
|
|
|
# Fetch latest release version from GitHub
|
|
function Get-LatestVersion {
|
|
Write-Host "Fetching latest release version..."
|
|
|
|
try {
|
|
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/$Script:REPO/releases/latest" -Method Get
|
|
$latestVersion = $response.tag_name
|
|
|
|
if (-not $latestVersion) {
|
|
Stop-WithError "Failed to fetch latest release version from GitHub"
|
|
}
|
|
|
|
return $latestVersion
|
|
}
|
|
catch {
|
|
Stop-WithError "Failed to fetch latest release version: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# Validate version format
|
|
function Test-VersionFormat {
|
|
param([string]$VersionString)
|
|
|
|
# Check if version matches semantic versioning (with or without 'v' prefix)
|
|
if ($VersionString -notmatch '^v?\d+\.\d+\.\d+(-.*)?$') {
|
|
Stop-WithError "Invalid version format: $VersionString`nExpected format: v1.0.0 or 1.0.0"
|
|
}
|
|
|
|
# Ensure version starts with 'v' for GitHub releases
|
|
if ($VersionString -notmatch '^v') {
|
|
$VersionString = "v$VersionString"
|
|
}
|
|
|
|
return $VersionString
|
|
}
|
|
|
|
# Check if coolify is already installed
|
|
function Test-ExistingInstallation {
|
|
param([string]$NewVersion)
|
|
|
|
try {
|
|
$coolifyCmd = Get-Command coolify -ErrorAction SilentlyContinue
|
|
if ($coolifyCmd) {
|
|
$currentVersion = & coolify version 2>$null | Select-Object -First 1
|
|
if (-not $currentVersion) {
|
|
$currentVersion = "unknown"
|
|
}
|
|
|
|
Write-Warning "Coolify CLI is already installed: $currentVersion"
|
|
Write-Host "This will upgrade/reinstall to version " -NoNewline
|
|
Write-Success $NewVersion
|
|
|
|
$response = Read-Host "Continue? [y/N]"
|
|
if ($response -notmatch '^[Yy]$') {
|
|
Write-Host "Installation cancelled."
|
|
exit 0
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
# Coolify not found, continue with installation
|
|
}
|
|
}
|
|
|
|
# Download and install binary from GitHub release
|
|
function Install-FromGitHub {
|
|
param(
|
|
[string]$Repo,
|
|
[string]$Release,
|
|
[string]$Name,
|
|
[string]$InstallDirectory,
|
|
[hashtable]$Platform
|
|
)
|
|
|
|
# Clean version (remove 'v' prefix if present)
|
|
$cleanVersion = $Release -replace '^v', ''
|
|
|
|
$filename = "${Name}_${cleanVersion}_$($Platform.OS)_$($Platform.Arch).zip"
|
|
$downloadUrl = "https://github.com/${Repo}/releases/download/${Release}/${filename}"
|
|
|
|
Write-Success "Downloading $Name $Release"
|
|
Write-Host "Platform: $($Platform.OS)/$($Platform.Arch)"
|
|
Write-Host "URL: $downloadUrl"
|
|
|
|
# Create temp file
|
|
$Script:TEMP_FILE = [System.IO.Path]::GetTempFileName() + ".zip"
|
|
|
|
# Download file
|
|
try {
|
|
$ProgressPreference = 'SilentlyContinue' # Speed up download
|
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $Script:TEMP_FILE -ErrorAction Stop
|
|
$ProgressPreference = 'Continue'
|
|
}
|
|
catch {
|
|
Stop-WithError "Failed to download from ${downloadUrl}`nPlease check if the version exists or try again later.`nError: $($_.Exception.Message)"
|
|
}
|
|
|
|
# Verify downloaded file is not empty
|
|
if ((Get-Item $Script:TEMP_FILE).Length -eq 0) {
|
|
Stop-WithError "Downloaded file is empty"
|
|
}
|
|
|
|
# Create install directory if it doesn't exist
|
|
if (-not (Test-Path $InstallDirectory)) {
|
|
Write-Host "Creating directory: $InstallDirectory"
|
|
try {
|
|
New-Item -ItemType Directory -Path $InstallDirectory -Force | Out-Null
|
|
}
|
|
catch {
|
|
Stop-WithError "Failed to create directory $InstallDirectory : $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# Extract binary
|
|
Write-Host "Installing $Name to $InstallDirectory\$Script:BINARY_NAME"
|
|
|
|
try {
|
|
# Remove existing binary if present
|
|
$binaryPath = Join-Path $InstallDirectory $Script:BINARY_NAME
|
|
if (Test-Path $binaryPath) {
|
|
Remove-Item -Path $binaryPath -Force
|
|
}
|
|
|
|
# Extract zip file
|
|
Expand-Archive -Path $Script:TEMP_FILE -DestinationPath $InstallDirectory -Force
|
|
}
|
|
catch {
|
|
Stop-WithError "Failed to extract binary: $($_.Exception.Message)"
|
|
}
|
|
|
|
# Verify installation
|
|
$installedBinary = Join-Path $InstallDirectory $Script:BINARY_NAME
|
|
if (-not (Test-Path $installedBinary)) {
|
|
Stop-WithError "Binary was not installed to $installedBinary"
|
|
}
|
|
|
|
Write-Success "✓ $Name installed successfully to $installedBinary"
|
|
|
|
# Add to PATH if not already present
|
|
Add-ToPath -Directory $InstallDirectory
|
|
|
|
# Show installed version
|
|
try {
|
|
$installedVersion = & $installedBinary version 2>$null | Select-Object -First 1
|
|
if ($installedVersion) {
|
|
Write-Host "Installed version: " -NoNewline
|
|
Write-Success $installedVersion
|
|
}
|
|
}
|
|
catch {
|
|
# Version check failed, but installation was successful
|
|
}
|
|
}
|
|
|
|
# Add directory to PATH
|
|
function Add-ToPath {
|
|
param([string]$Directory)
|
|
|
|
# Determine which PATH to modify (user or system)
|
|
$pathScope = if ($User -or -not (Test-Administrator)) { "User" } else { "Machine" }
|
|
|
|
# Get current PATH
|
|
$currentPath = [Environment]::GetEnvironmentVariable("Path", $pathScope)
|
|
|
|
# Check if directory is already in PATH
|
|
$pathDirs = $currentPath -split ';' | ForEach-Object { $_.Trim() }
|
|
if ($pathDirs -contains $Directory) {
|
|
Write-Host "Directory is already in PATH"
|
|
return
|
|
}
|
|
|
|
Write-Host "Adding $Directory to PATH ($pathScope)..."
|
|
|
|
try {
|
|
# Add to PATH
|
|
$newPath = "$currentPath;$Directory"
|
|
[Environment]::SetEnvironmentVariable("Path", $newPath, $pathScope)
|
|
|
|
# Update current session PATH
|
|
$env:Path = "$env:Path;$Directory"
|
|
|
|
Write-Success "✓ Added to PATH. You may need to restart your terminal for changes to take effect."
|
|
}
|
|
catch {
|
|
Write-Warning "Failed to add to PATH automatically: $($_.Exception.Message)"
|
|
Write-Host "Please add the following directory to your PATH manually:"
|
|
Write-Host " $Directory"
|
|
}
|
|
}
|
|
|
|
# Main installation flow
|
|
function Install-CoolifyCLI {
|
|
Write-Host "Coolify CLI Installer for Windows"
|
|
Write-Host "=================================="
|
|
Write-Host ""
|
|
|
|
# Show help if requested
|
|
if ($Help) {
|
|
Show-Help
|
|
}
|
|
|
|
# Detect platform
|
|
$platform = Get-PlatformInfo
|
|
|
|
# Determine version to install
|
|
$versionToInstall = if ($Version) {
|
|
Test-VersionFormat -VersionString $Version
|
|
} else {
|
|
Get-LatestVersion
|
|
}
|
|
|
|
Write-Host "Version to install: $versionToInstall"
|
|
Write-Host ""
|
|
|
|
# Check existing installation
|
|
Test-ExistingInstallation -NewVersion $versionToInstall
|
|
|
|
# Determine install directory
|
|
$installDirectory = if ($InstallDir) {
|
|
$InstallDir
|
|
} elseif ($User) {
|
|
$Script:USER_INSTALL_DIR
|
|
} else {
|
|
$Script:GLOBAL_INSTALL_DIR
|
|
}
|
|
|
|
# Check for admin rights if installing globally
|
|
if (-not $User -and -not $InstallDir -and -not (Test-Administrator)) {
|
|
Write-Warning "Global installation requires administrator privileges."
|
|
Write-Host "Please run this script as Administrator, or use -User flag for user installation."
|
|
Write-Host ""
|
|
$response = Read-Host "Switch to user installation? [Y/n]"
|
|
if ($response -match '^[Nn]$') {
|
|
Stop-WithError "Installation cancelled. Please run as Administrator for global installation."
|
|
}
|
|
$installDirectory = $Script:USER_INSTALL_DIR
|
|
}
|
|
|
|
$installMode = if ($User -or $installDirectory -eq $Script:USER_INSTALL_DIR) {
|
|
"User (no admin rights required)"
|
|
} else {
|
|
"Global (administrator)"
|
|
}
|
|
|
|
Write-Host "Install mode: $installMode"
|
|
Write-Host "Install directory: $installDirectory"
|
|
Write-Host ""
|
|
|
|
# Download and install
|
|
Install-FromGitHub -Repo $Script:REPO -Release $versionToInstall -Name "coolify-cli" -InstallDirectory $installDirectory -Platform $platform
|
|
|
|
Write-Host ""
|
|
Write-Success "Installation complete!"
|
|
Write-Host "Run 'coolify --help' to get started"
|
|
Write-Host ""
|
|
Write-Host "Note: If 'coolify' command is not found, please restart your terminal."
|
|
}
|
|
|
|
# Run main installation
|
|
try {
|
|
Install-CoolifyCLI
|
|
}
|
|
catch {
|
|
Write-ErrorMessage "Unexpected error: $($_.Exception.Message)"
|
|
Write-ErrorMessage $_.ScriptStackTrace
|
|
Cleanup
|
|
}
|
|
finally {
|
|
Cleanup
|
|
}
|