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).
76 lines
2.7 KiB
PowerShell
76 lines
2.7 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Regression tests for install.ps1.
|
|
|
|
.BUG
|
|
`irm <url> | iex` closed the terminal window. Cause: the script used
|
|
`exit`, which under Invoke-Expression terminates the entire PowerShell
|
|
host process (there is no script scope to exit). Fix: use `return`
|
|
(success) / `throw` (errors) so the host survives.
|
|
|
|
.NOTES
|
|
Host-process termination cannot be observed from inside the dying process,
|
|
so these tests combine a source-verification guard with a behavioral test
|
|
run in a child process (`exit` cannot be caught by try/catch and kills the
|
|
child; `throw` is catchable).
|
|
#>
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
$installScript = Join-Path $repoRoot "install.ps1"
|
|
$failures = [System.Collections.Generic.List[string]]::new()
|
|
|
|
function Assert-True([bool]$Cond, [string]$Message) {
|
|
if (-not $Cond) { $failures.Add($Message) | Out-Null }
|
|
}
|
|
|
|
# --- Test 1 (source verification): no host-terminating `exit` ---
|
|
$content = Get-Content -Path $installScript -Raw
|
|
$exitCount = ([regex]::Matches($content, '(?m)^\s*exit\b')).Count
|
|
Assert-True ($exitCount -eq 0) "install.ps1 must not use bare 'exit' (found $exitCount); 'irm | iex' would kill the host. Use return/throw."
|
|
|
|
# --- Test 2: parses cleanly ---
|
|
$parseOk = $true
|
|
try { [void][scriptblock]::Create($content) } catch { $parseOk = $false }
|
|
Assert-True $parseOk "install.ps1 must parse without syntax errors."
|
|
|
|
# --- Test 3 (behavioral): iex must NOT kill the host ---
|
|
# Force the version lookup to fail fast against an unreachable address so the
|
|
# script reaches its early-exit/throw path offline. `exit` terminates the child
|
|
# (RESULT never emitted); `throw` is caught and RESULT is emitted.
|
|
$inner = @'
|
|
$ErrorActionPreference = "Stop"
|
|
$env:OCTOPUS_DOWNLOAD_BASE = "http://127.0.0.1:1"
|
|
$r = "nothing"
|
|
try {
|
|
Invoke-Expression (Get-Content -Raw "__INSTALL__")
|
|
$r = "completed"
|
|
} catch {
|
|
$r = "caught-throw"
|
|
}
|
|
Write-Output ("RESULT=$r")
|
|
'@.Replace("__INSTALL__", $installScript)
|
|
|
|
$tmp = Join-Path $env:TEMP ("octopus_iex_test_" + $PID + ".ps1")
|
|
Set-Content -Path $tmp -Value $inner -Encoding UTF8
|
|
try {
|
|
$childOut = (& powershell -NoProfile -File $tmp 2>&1 | Out-String)
|
|
} finally {
|
|
Remove-Item $tmp -ErrorAction SilentlyContinue
|
|
}
|
|
Assert-True ($childOut -match 'RESULT=') "iex must not terminate the host (try/catch must observe a RESULT). Child output:`n$childOut"
|
|
|
|
# --- Report ---
|
|
if ($failures.Count -gt 0) {
|
|
Write-Host ("FAIL: " + $failures.Count + " test(s)") -ForegroundColor Red
|
|
foreach ($f in $failures) { Write-Host (" - " + $f) -ForegroundColor Red }
|
|
exit 1
|
|
}
|
|
Write-Host "PASS: all regression tests passed." -ForegroundColor Green
|
|
exit 0
|