# ============================================================================= # vxcli universal installer — Windows (PowerShell 5.1+ / 7+) # # Usage (the line you publish on vxcloud.io/download/cli): # irm https://vxcloud.io/download/cli/install.ps1 | iex # # Options (environment variables, set before the command): # $env:VXCLI_VERSION = "19.8.9" # specific version (default: latest) # $env:VXCLI_INSTALL_DIR = "C:\path" # install dir (default: %LOCALAPPDATA%\Programs\vxcli) # $env:VXCLI_BASE_URL = "http://..." # override host (for local testing) # # No admin rights needed: installs to a per-user dir and updates the User PATH. # Verifies sha256 before installing. # ============================================================================= $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $BaseUrl = if ($env:VXCLI_BASE_URL) { $env:VXCLI_BASE_URL.TrimEnd('/') } else { 'https://vxcloud.io/download/cli' } $InstallDir = if ($env:VXCLI_INSTALL_DIR) { $env:VXCLI_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\vxcli' } function Info($m) { Write-Host "==> $m" -ForegroundColor Blue } function Ok($m) { Write-Host " ok $m" -ForegroundColor Green } function Warn($m) { Write-Host "warn $m" -ForegroundColor Yellow } function Die($m) { Write-Host "error $m" -ForegroundColor Red; exit 1 } # ── detect architecture ── $arch = switch ($env:PROCESSOR_ARCHITECTURE) { 'AMD64' { 'amd64' } 'ARM64' { 'arm64' } # NOTE: ship a windows/arm64 asset if you target ARM laptops default { Die "unsupported architecture: $($env:PROCESSOR_ARCHITECTURE)" } } # ── resolve version ── $version = $env:VXCLI_VERSION if (-not $version) { Info 'Resolving latest version...' try { $version = (Invoke-RestMethod "$BaseUrl/stable.txt").ToString().Trim() } catch { Die "could not read latest version from $BaseUrl/stable.txt" } } Info "Installing vxcli v$version for windows/$arch" $asset = "vxcli_${version}_windows_${arch}.exe" $assetUrl = "$BaseUrl/v$version/$asset" $sumsUrl = "$BaseUrl/v$version/checksums.txt" $tmp = Join-Path ([IO.Path]::GetTempPath()) ("vxcli_" + [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp -Force | Out-Null $tmpExe = Join-Path $tmp 'vxcli.exe' try { Info "Downloading $asset ..." Invoke-WebRequest -Uri $assetUrl -OutFile $tmpExe -UseBasicParsing # ── verify sha256 ── try { $sums = (Invoke-WebRequest -Uri $sumsUrl -UseBasicParsing).Content $line = $sums -split "`n" | Where-Object { $_ -match [Regex]::Escape($asset) } | Select-Object -First 1 if ($line) { $expected = ($line -split '\s+')[0].ToLower() $actual = (Get-FileHash -Algorithm SHA256 $tmpExe).Hash.ToLower() if ($expected -ne $actual) { Die "checksum mismatch! expected $expected got $actual" } Ok 'Checksum verified' } else { Warn "no checksum entry for $asset — skipping verification" } } catch { Warn 'checksums.txt unavailable — skipping verification' } # ── install ── New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null $dest = Join-Path $InstallDir 'vxcli.exe' Move-Item -Force $tmpExe $dest Unblock-File $dest # clear the downloaded "mark of the web" so it runs without a SmartScreen prompt Ok "Installed to $dest" # ── add to User PATH (idempotent) ── $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') if (($userPath -split ';') -notcontains $InstallDir) { [Environment]::SetEnvironmentVariable('Path', "$userPath;$InstallDir", 'User') $env:Path = "$env:Path;$InstallDir" # make it usable in THIS session too Ok "Added $InstallDir to your User PATH" Warn 'Open a NEW terminal for PATH changes to apply everywhere.' } Write-Host '' Ok "vxcli v$version installed. Run: vxcli version" } finally { Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue }