From 06696eede67e3256cac1af7f5f921029ae9ce9e8 Mon Sep 17 00:00:00 2001 From: borjaperfra Date: Mon, 14 Sep 2026 13:30:34 +0200 Subject: [PATCH] fix(install): stop leaving the caller's shell broken Reported: after the installer finishes, the terminal shows a bare `PS>` and nothing typed at it does anything. Second report of the same thing, and it was never Warp. `iex` runs this script in the caller's session, so the two preference assignments at the top of it were assignments to their shell, for the rest of its life. $ErrorActionPreference = 'Stop' left behind that way turns every later non-terminating error in that session into a terminating one - including inside the `prompt` function a terminal like Warp installs to know where a command begins and ends. When that throws, PowerShell falls back to its built-in `PS>` prompt and the terminal has lost the session: the prompt sits there and the keyboard goes nowhere. Confirmed rather than guessed, both halves: '$ErrorActionPreference = ''Stop''' | iex leaves the session on Stop a prompt function with a non-terminating error throws under Stop, and does not under Continue Inside a function the same assignment is local and goes away with the call, so that is where both of them live now. Verified: the session reads Continue on both preferences after a full install. CI refuses any preference variable assigned at script scope, which is the class rather than the instance. Seen failing against a reintroduced one. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 21 ++++++++++++++++++++- internal/tui/tui.go | 2 +- scripts/install.ps1 | 20 +++++++++++++++++--- scripts/install.sh | 2 +- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41e9585..212e2ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,4 +158,23 @@ jobs: exit 1 } } - Write-Host "installer parses, web requests basic-parsed, no unguarded exit, arch detection survives without .NET" + # Nothing may set a preference variable at script scope. `iex` runs + # this in the caller's session, so an assignment out there is an + # assignment to their shell for the rest of its life. + # $ErrorActionPreference = 'Stop' left behind turns every later + # non-terminating error in that session terminating - including + # inside the prompt function a terminal like Warp installs to know + # where a command begins and ends. When that throws, PowerShell + # falls back to its built-in PS> and the terminal loses the session: + # a prompt sits there and nothing typed at it does anything. + $depth = 0 + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + if ($line -match '^\s*#') { continue } + if ($depth -eq 0 -and $line -match '^\s*\$(ErrorAction|Progress|Warning|Information|Debug|Verbose|Confirm)Preference\s*=') { + Write-Host "::error file=scripts/install.ps1,line=$($i + 1)::a preference set at script scope follows the member into their shell; set it inside a function" + exit 1 + } + $depth += ([regex]::Matches($line, '{')).Count - ([regex]::Matches($line, '}')).Count + } + Write-Host "installer parses, web requests basic-parsed, no unguarded exit, arch survives without .NET, no preference leaks" diff --git a/internal/tui/tui.go b/internal/tui/tui.go index b9e9e13..d562764 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -2351,7 +2351,7 @@ func (m model) renderSetup(l layout) string { // ── about renderer ─────────────────────────────────────────────────────────── -const Version = "0.1.9" +const Version = "0.1.10" func renderAbout(l layout) string { var b strings.Builder diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 8dc5f51..f14940a 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -36,8 +36,18 @@ param( [string]$InstallDir = $env:NAN_INSTALL_DIR ) -$ErrorActionPreference = 'Stop' -$ProgressPreference = 'SilentlyContinue' # Write-Progress is slow over a pipe +# The preferences below are set INSIDE the function, not here. +# +# `iex` runs this in the caller's session, so an assignment at this level is an +# assignment to their shell, for the rest of its life. $ErrorActionPreference = +# 'Stop' left behind that way turns every later non-terminating error in that +# session into a terminating one - including inside the `prompt` function a +# terminal like Warp installs to know where a command begins and ends. When +# that throws, PowerShell falls back to its built-in `PS>` and the terminal +# loses track of the session: the prompt is there and nothing typed at it does +# anything. Which is exactly what was reported, twice. +# +# Inside a function the same assignment is local and goes away with the call. $Repo = 'helmcode/nan-cli' @@ -108,7 +118,7 @@ function Get-LatestVersion { could not work out the latest version from the GitHub API it rate limits unauthenticated requests, so this is usually temporary wait a few minutes, or pick a version yourself: - & ([scriptblock]::Create((irm https://nan.builders/install.ps1))) -Version v0.1.9 + & ([scriptblock]::Create((irm https://nan.builders/install.ps1))) -Version v0.1.10 the releases are at https://github.com/$Repo/releases "@ } @@ -140,6 +150,10 @@ function Add-ToUserPath($dir) { function Install-NanCli { param([string]$Version, [string]$InstallDir) + # Local to this call. See the note where these used to live. + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' # Write-Progress is slow over a pipe + $arch = Get-Arch if (-not $Version) { Write-Step 'fetching latest release...' diff --git a/scripts/install.sh b/scripts/install.sh index 9176607..5937628 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -62,7 +62,7 @@ require_version() { err "could not work out the latest version from the GitHub API" err "it rate limits unauthenticated requests, so this is usually temporary" err "wait a few minutes, or pick a version yourself:" - printf " VERSION=v0.1.9 curl -fsSL https://nan.builders/install | bash + printf " VERSION=v0.1.10 curl -fsSL https://nan.builders/install | bash " >&2 err "the releases are at https://github.com/$REPO/releases" exit 1