From c096847d23ffc6153cc5ac024f59f383897cb737 Mon Sep 17 00:00:00 2001 From: sunasrd-byte Date: Wed, 12 Aug 2026 11:32:54 +0200 Subject: [PATCH] fix: don't kill the user's shell when the installer aborts The documented install path is `irm https://codeplain.ai/install.ps1 | iex`, which runs the script in the caller's scope. `exit 1` there terminates the interactive session, so the window closes before the user can read why. On a fresh machine without git, Assert-Git printed the install instructions and then destroyed the window mid-sentence. Route both abort paths through Stop-Install, which sets $LASTEXITCODE and calls `exit` only when the run has no interactive window to lose: a file-based run ($PSCommandPath) or an unattended one ($nonInteractive). Unattended runs need it because `pwsh -Command "irm ... | iex"` takes its process exit code from `exit` and ignores $LASTEXITCODE, so a CI install that aborted here would otherwise report success. Callers `return` to stop the remaining script. Also build the git-detected check mark from its code point. A literal U+2713 in a BOM-less file is read as CP1252 by Windows PowerShell 5.1, where it decodes to a smart quote that terminates the string; the lint-powershell workflow already forbids this. --- install/powershell/install.ps1 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/install/powershell/install.ps1 b/install/powershell/install.ps1 index 89bc7a9..7f9f2e3 100644 --- a/install/powershell/install.ps1 +++ b/install/powershell/install.ps1 @@ -129,7 +129,14 @@ function Test-GitAvailable { } } -# Print install instructions for the current platform and exit. codeplain uses +# `exit` under `irm ... | iex` closes the user's shell, so exit only for file-based +# ($PSCommandPath) or unattended runs. Callers must `return` right after this. +function Stop-Install { + $global:LASTEXITCODE = 1 + if ($PSCommandPath -or $nonInteractive) { exit 1 } +} + +# Print install instructions for the current platform. codeplain uses # git to checkpoint the code it renders, and GitPython fails at import time # when the git executable is missing, so 'codeplain --status' would die with a # raw traceback that says nothing about the actual cause. @@ -148,7 +155,7 @@ function Assert-Git { Write-Host "" Write-Host " ${GRAY}Once Git is installed, restart your terminal and run this installer again.${NC}" Write-Host "" - exit 1 + Stop-Install } # Verify an API key against the Codeplain API's /status endpoint. @@ -193,9 +200,10 @@ Write-Host "" # before the user answers any prompts rather than at the --status check below. if (-not (Test-GitAvailable)) { Assert-Git + return } -Write-Host "${GREEN}✓${NC} git detected" +Write-Host "${GREEN}${CHECK}${NC} git detected" Write-Host "" try { @@ -545,7 +553,8 @@ if ($env:CODEPLAIN_API_KEY) { Write-Host $verifyOutput Write-Host "${GRAY}Please restart your terminal and try again, or reinstall with:${NC}" Write-Host " uv tool install --force codeplain" - exit 1 + Stop-Install + return } }