Files
octopus-release/install.ps1
T
octopus-agent fbac1003f0 fix(install): do not kill PowerShell host under 'irm | iex'
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).
2026-07-02 09:10:03 +08:00

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