The Windows installer used 'exit' inside Install-Octopus. When run via
'irm <url> | iex' (the documented one-liner), there is no script scope, so
'exit' terminates the entire PowerShell host process and closes the terminal
window instead of just ending the install. Most users hit this via the
'already installed' branch (exit 0).
Replace 'exit' with:
- 'return' for the success (already-installed) path
- 'throw' for error paths (still non-zero exit when run as .\install.ps1
in CI; prints the error and returns to the prompt under iex)
Add tests/test.ps1 as a regression guard (source-verification + a behavioral
test proving iex no longer terminates the host).
196 lines
7.0 KiB
PowerShell
196 lines
7.0 KiB
PowerShell
#Requires -Version 5.1
|
|
# Public release repository: http://octopus.51zxtx.com:3000/fourbroad/octopus-release
|
|
<#
|
|
.SYNOPSIS
|
|
Octopus installer for Windows.
|
|
|
|
.DESCRIPTION
|
|
Downloads and installs the Octopus CLI from Gitea Releases.
|
|
|
|
.PARAMETER Version
|
|
Install a specific version (e.g. 1.0.180). Defaults to latest.
|
|
|
|
.PARAMETER NoModifyPath
|
|
Do not add the install directory to the user PATH.
|
|
|
|
.EXAMPLE
|
|
irm http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/dev/install.ps1 | iex
|
|
|
|
.EXAMPLE
|
|
irm http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/dev/install.ps1 | iex; Install-Octopus -Version 0.1.3
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$Version = "",
|
|
[switch]$NoModifyPath
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$APP = "octopus"
|
|
$REPO = "fourbroad/octopus-release"
|
|
$baseUrl = if ($env:OCTOPUS_DOWNLOAD_BASE) { $env:OCTOPUS_DOWNLOAD_BASE } else { "http://octopus.51zxtx.com:3000" }
|
|
$INSTALL_DIR = Join-Path $env:USERPROFILE ".octopus\bin"
|
|
|
|
function Write-Color([string]$Text, [string]$Color = "White") {
|
|
Write-Host $Text -ForegroundColor $Color
|
|
}
|
|
|
|
function Get-Arch {
|
|
$arch = $env:PROCESSOR_ARCHITECTURE
|
|
if ($arch -eq "AMD64") { return "x64" }
|
|
if ($arch -eq "ARM64") { return "arm64" }
|
|
throw "Unsupported architecture: $arch"
|
|
}
|
|
|
|
function Test-Avx2Support {
|
|
try {
|
|
Add-Type -MemberDefinition @"
|
|
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
|
|
public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);
|
|
"@ -Name "Kernel32" -Namespace "Win32" -ErrorAction SilentlyContinue | Out-Null
|
|
# PF_AVX2_INSTRUCTIONS_AVAILABLE = 40
|
|
return [Win32.Kernel32]::IsProcessorFeaturePresent(40)
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Get-LatestVersion {
|
|
# Resolve the latest version by following the /releases/latest redirect to
|
|
# /releases/tag/vX.Y.Z. This avoids the API, which is
|
|
# rate-limited to 60 requests/hour per IP.
|
|
$latestUrl = "$baseUrl/$REPO/releases/latest"
|
|
$location = $null
|
|
$request = [System.Net.HttpWebRequest]::Create($latestUrl)
|
|
$request.Method = "HEAD"
|
|
$request.AllowAutoRedirect = $false
|
|
$request.UserAgent = "octopus-installer"
|
|
try {
|
|
$response = $request.GetResponse()
|
|
$location = $response.Headers["Location"]
|
|
$response.Close()
|
|
} catch [System.Net.WebException] {
|
|
$resp = $_.Exception.Response
|
|
if ($null -ne $resp) {
|
|
$location = $resp.Headers["Location"]
|
|
$resp.Close()
|
|
}
|
|
}
|
|
if (-not $location) { return "" }
|
|
if ($location -match '/tag/v?([^/]+)/?$') {
|
|
return $Matches[1] -replace '^v', ''
|
|
}
|
|
return ""
|
|
}
|
|
|
|
function Install-Octopus {
|
|
$arch = Get-Arch
|
|
|
|
# Resolve version
|
|
if ($Version -ne "") {
|
|
$specificVersion = $Version -replace '^v', ''
|
|
# Verify release exists
|
|
$releaseUrl = "$baseUrl/$REPO/releases/tag/v$specificVersion"
|
|
try {
|
|
$null = Invoke-WebRequest -Uri $releaseUrl -Method Head -UseBasicParsing -ErrorAction Stop
|
|
} catch {
|
|
throw "Release v$specificVersion not found. Available releases: $baseUrl/$REPO/releases"
|
|
}
|
|
} else {
|
|
Write-Color "Fetching latest version..." "DarkGray"
|
|
$specificVersion = Get-LatestVersion
|
|
if (-not $specificVersion) {
|
|
throw "Failed to fetch latest version."
|
|
}
|
|
}
|
|
|
|
# Check if already installed at this version
|
|
$existing = Join-Path $INSTALL_DIR "$APP.exe"
|
|
if (Test-Path $existing) {
|
|
try {
|
|
$installedVersion = & $existing --version 2>$null
|
|
if ($installedVersion -eq $specificVersion) {
|
|
Write-Color "Version $specificVersion already installed." "DarkGray"
|
|
return
|
|
} else {
|
|
Write-Color "Installed version: $installedVersion" "DarkGray"
|
|
}
|
|
} catch { }
|
|
}
|
|
|
|
# Build filename
|
|
$target = "$APP-windows-$arch"
|
|
if ($arch -eq "x64" -and -not (Test-Avx2Support)) {
|
|
$target = "$APP-windows-$arch-baseline"
|
|
}
|
|
$filename = "$target.zip"
|
|
$downloadUrl = "$baseUrl/$REPO/releases/download/v$specificVersion/$filename"
|
|
|
|
Write-Color ""
|
|
Write-Color "Installing octopus version: $specificVersion" "DarkGray"
|
|
Write-Color "Downloading $filename ..." "DarkGray"
|
|
|
|
# Download
|
|
$tmpDir = Join-Path $env:TEMP "octopus_install_$PID"
|
|
New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
|
|
$zipPath = Join-Path $tmpDir $filename
|
|
|
|
try {
|
|
$ProgressPreference = "SilentlyContinue"
|
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing
|
|
} catch {
|
|
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
|
|
throw "Failed to download $downloadUrl : $($_.Exception.Message)"
|
|
}
|
|
|
|
# Extract
|
|
Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force
|
|
|
|
# Install
|
|
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
|
|
$exeSrc = Join-Path $tmpDir "$APP.exe"
|
|
if (-not (Test-Path $exeSrc)) {
|
|
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
|
|
throw "$APP.exe not found in archive."
|
|
}
|
|
Copy-Item -Path $exeSrc -Destination (Join-Path $INSTALL_DIR "$APP.exe") -Force
|
|
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
|
|
|
|
# Add to PATH
|
|
if (-not $NoModifyPath) {
|
|
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
if ($userPath -notlike "*$INSTALL_DIR*") {
|
|
[Environment]::SetEnvironmentVariable("PATH", "$INSTALL_DIR;$userPath", "User")
|
|
Write-Color "Added $INSTALL_DIR to user PATH." "DarkGray"
|
|
Write-Color "Restart your terminal for PATH changes to take effect." "Yellow"
|
|
}
|
|
}
|
|
|
|
# CI Actions support (GitHub/Gitea)
|
|
if ($env:GITHUB_ACTIONS -eq "true" -and $env:GITHUB_PATH) {
|
|
Add-Content -Path $env:GITHUB_PATH -Value $INSTALL_DIR
|
|
Write-Color "Added $INSTALL_DIR to GITHUB_PATH." "DarkGray"
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host " ___ _" -ForegroundColor DarkYellow
|
|
Write-Host " / _ \ ___| |_ ___ _ __ _ _ ___" -ForegroundColor DarkYellow
|
|
Write-Host "| | | |/ __| __/ _ \| '_ \| | | / __|" -ForegroundColor Yellow
|
|
Write-Host "| |_| | (__| || (_) | |_) | |_| \__ \" -ForegroundColor Yellow
|
|
Write-Host " \___/ \___|\__\___/| .__/ \__,_|___/" -ForegroundColor White
|
|
Write-Host " |_|" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host "Octopus includes free models, to start:" -ForegroundColor DarkGray
|
|
Write-Host ""
|
|
Write-Host " cd <project> " -NoNewline; Write-Host "# Open directory" -ForegroundColor DarkGray
|
|
Write-Host " octopus " -NoNewline; Write-Host "# Run command" -ForegroundColor DarkGray
|
|
Write-Host ""
|
|
Write-Host "For more information visit " -NoNewline -ForegroundColor DarkGray
|
|
Write-Host "https://octopus.51zxtx.com/docs"
|
|
Write-Host ""
|
|
}
|
|
|
|
Install-Octopus
|