Skip to content

Make the uninstaller test suites run on macOS and Windows PowerShell 5.1 - #7643

Merged
danielhanchen merged 1 commit into
mainfrom
fix-uninstaller-test-portability
Jul 30, 2026
Merged

Make the uninstaller test suites run on macOS and Windows PowerShell 5.1#7643
danielhanchen merged 1 commit into
mainfrom
fix-uninstaller-test-portability

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Problem

The uninstaller suites added in #7631 pass on the ubuntu runner, which is the only place CI runs them. On the other two platforms they abort before finishing.

Verified on real runners (macos-14, windows-latest, and Windows PowerShell 5.1) before this change:

Suite ubuntu-latest macos-14 Windows PowerShell 5.1
tests/sh/test_uninstall_arg_guard.sh 25 passed aborts at the first case n/a
tests/studio/test_uninstall_arg_guard.ps1 39 checks 39 checks aborts at the first case

Root causes

macOS. make_home built its fixture with TMPDIR="$_TMP_ROOT" mktemp -d. BSD mktemp with no template implies -t and resolves the directory itself, so the result lands beside _TMP_ROOT instead of inside it. The validation immediately below then fires and exit 1s the whole suite:

FATAL: no fixture home under /var/folders/.../T/tmp.iWsJO96MVD (got '/var/folders/.../T/tmp.15xzcgEyvp')

An explicit template is portable across GNU and BSD and keeps the existing containment check meaningful.

tests/sh/test_uninstall_shared_icon.sh had the same class of problem with mktemp -p, which is GNU only.

Windows PowerShell 5.1. The suite sets $ErrorActionPreference = "Stop" and then merges a native command's stderr with 2>&1. Windows PowerShell turns that into a terminating NativeCommandError, so the run dies on the first rejected-argument case, which is the first case that writes to stderr:

powershell.exe : __UNSLOTH_TEST_BODY_REACHED__
+ $out = & $pwshPath @argv 2>&1 | Out-String
+ FullyQualifiedErrorId : NativeCommandError

PowerShell 7.1 and later do not do this, which is why the pwsh runs are green. The preference is now scoped down around the two native invocations.

Why 5.1 matters here

The suite is self-hosting through (Get-Process -Id $PID).Path, so launching it from powershell.exe exercises 5.1, and 5.1 is the shell the README one-liner lands in on a stock Windows box. Nothing in CI ran 5.1 before, which is why this went unnoticed, so the Windows workflow now runs the guard suite there too.

Verification

Re-run on the same runners after the change:

Job Before After
shell suites (ubuntu-latest) pass pass
shell suites (macos-14) fail pass
powershell suites (ubuntu-latest / macos-14 / windows-latest) pass pass
Windows PowerShell 5.1 argument guard fail pass

Locally: test_uninstall_arg_guard.sh 25 passed, test_uninstall_shared_icon.sh 11 passed, test_uninstall_arg_guard.ps1 39 checks passed; dash -n / bash -n / busybox sh -n clean and the ps1 parses; tests/studio/test_ci_shell_suite_coverage.py 15 passed.

No production code changes. scripts/uninstall.sh and scripts/uninstall.ps1 are untouched.

The suites added in #7631 pass on the ubuntu runner but abort elsewhere.

tests/sh/test_uninstall_arg_guard.sh built its fixture with
TMPDIR="$_TMP_ROOT" mktemp -d. BSD mktemp with no template implies -t and
resolves the directory itself, so on macOS the result is a sibling of
_TMP_ROOT rather than a child, the validation below it fires, and the whole
suite exits at the first --help case. Pass an explicit template instead.

tests/sh/test_uninstall_shared_icon.sh used mktemp -p, which is GNU only.

tests/studio/test_uninstall_arg_guard.ps1 set $ErrorActionPreference to Stop
and then merged a native command's stderr with 2>&1. Windows PowerShell 5.1
turns that into a terminating NativeCommandError, so the suite died on the
first rejected-argument case; PowerShell 7.1 and later do not. Scope the
preference down around the two native invocations.

The suite is self-hosting through (Get-Process -Id $PID).Path, so the Windows
workflow now also runs it under powershell.exe. Nothing in CI covered 5.1
before, which is why this went unnoticed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 958207de55

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

function Invoke-Uninstaller([string[]]$ScriptArgs) {
$argv = @("-NoProfile", "-File", $uninstallPath) + $ScriptArgs
$out = & $pwshPath @argv 2>&1 | Out-String
$out = Invoke-Native { & $pwshPath @argv 2>&1 }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the native exit code across the helper scope

When Invoke-Uninstaller launches the child through Invoke-Native, the native process sets $LASTEXITCODE only in the helper's child scope; after the helper returns, line 59 therefore reads the caller's previous value (initially $null) rather than the uninstaller's exit code. Consequently every help case is recorded as nonzero because $null -eq 0 is false, the suite accumulates failures, and both Windows workflow invocations added by this change exit 1. Return/capture the exit code inside Invoke-Native along with the output instead of reading it afterward.

Useful? React with 👍 / 👎.

@danielhanchen
danielhanchen merged commit 779d8fa into main Jul 30, 2026
20 of 22 checks passed
@danielhanchen
danielhanchen deleted the fix-uninstaller-test-portability branch July 30, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant