From 517bbce25bc95578ad082447a67bd195cad46763 Mon Sep 17 00:00:00 2001 From: anandgupta42 Date: Sun, 21 Jun 2026 22:10:02 -0700 Subject: [PATCH] fix: [#958] make Windows installer Pester env injection deterministic The `Windows Installer (Pester)` CI job began failing on the v0.8.9 release commit although `install.ps1` and its tests were unchanged (the commit only touched `CHANGELOG.md` and renamed a test). The same code passed on the two prior pushes (#930, #946); the `windows-latest` runner image changed in between, and a re-run reproduced the failure deterministically. `Invoke-Installer` set `PROCESSOR_*` vars via `[Environment]::SetEnvironmentVariable` (Process scope) on the Pester host and spawned `& pwsh -File`, relying on inheritance. `PROCESSOR_ARCHITECTURE` is a loader-managed variable; the updated runner re-initializes it for spawned processes, so the override no longer reached the child (it arrived blank -> `Unsupported OS/Arch: windows/`). Custom vars like `PROCESSOR_ARCHITEW6432` are unaffected, which is why the WOW64 test kept passing. Apply the requested env vars inside the child's own session via a `pwsh -Command` preamble (after the loader runs), removing vars whose value is empty, and pass the script args as bareword command-line tokens so parameter names bind as names (matching the original `-File @ScriptArgs` semantics). Verified green on windows-latest: 9/9 Pester tests pass. This is a test-harness fix only - the shipped `install.ps1` and v0.8.9 binaries are correct; real users always have a populated `PROCESSOR_ARCHITECTURE`. Co-Authored-By: Claude Opus 4.8 (1M context) --- test/windows/install.Tests.ps1 | 39 +++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/test/windows/install.Tests.ps1 b/test/windows/install.Tests.ps1 index 9abea83e2b..118f7cbcd1 100644 --- a/test/windows/install.Tests.ps1 +++ b/test/windows/install.Tests.ps1 @@ -15,24 +15,43 @@ BeforeAll { # Invoke install.ps1 in a child pwsh with a controlled environment and return # @{ Code = ; Output = }. PROCESSOR_* env # vars are passed per-call so we can simulate WOW64 / ARM64 hosts. + # + # The requested env vars are applied INSIDE the child session (via a -Command + # preamble), not by mutating this host's Process-scope environment and relying + # on inheritance. PROCESSOR_ARCHITECTURE is a loader-managed variable: the + # windows-latest runner re-initializes it for a spawned process, so a + # Process-scope override here does not reliably reach `pwsh -File` (the child + # saw it blank). Setting it in the child's own session, after the loader has + # run, is deterministic. An empty value removes the var so detection of a + # "missing" PROCESSOR_ARCHITEW6432 falls through correctly. function Invoke-Installer { param( [string[]]$ScriptArgs = @(), [hashtable]$Env = @{} ) - $saved = @{} + # Single-quote PowerShell literals by doubling embedded single quotes. + $sq = "'"; $escSq = "''" + $preamble = "" foreach ($k in $Env.Keys) { - $saved[$k] = [Environment]::GetEnvironmentVariable($k) - [Environment]::SetEnvironmentVariable($k, $Env[$k]) - } - try { - $output = & pwsh -NoProfile -File $script:InstallScript @ScriptArgs 2>&1 | Out-String - return @{ Code = $LASTEXITCODE; Output = $output } - } finally { - foreach ($k in $Env.Keys) { - [Environment]::SetEnvironmentVariable($k, $saved[$k]) + $v = $Env[$k] + if ([string]::IsNullOrEmpty($v)) { + $preamble += "Remove-Item -Path Env:$k -ErrorAction SilentlyContinue; " + } else { + $vEsc = $v.Replace($sq, $escSq) + $preamble += "`$env:$k = '$vEsc'; " } } + # Pass the script args as bareword command-line tokens (e.g. `-Version + # 0.0.0-nonexistent`) so parameter NAMES bind as names - matching the + # original `pwsh -File