fix(tests): give path-persist a real red/green signal (off-PATH prefix + interactive per-shell) (#310)#313
Merged
Conversation
…ix + interactive per-shell (#310) As merged in #214 the path-persist guard could not distinguish a fixed installer from a broken one: - It ran as root, so cli/install.sh landed the binary in /usr/local/bin (on every PATH). The installer only PERSISTS a PATH entry for an off-PATH prefix, so no rc was ever written and every shell resolved the binary trivially — green on a broken installer too. - The login × non-login matrix mis-modelled 'open a new terminal': a non-interactive `bash -c` reads NO rc file, and a *login* bash reads ~/.profile, not the ~/.bashrc the installer writes on Linux. Rewrite to actually exercise persistence: - Pin INSTALL_PREFIX=$HOME/.local/bin (a $HOME dir the installer always persists, off the default root PATH). Precondition-check it's not already on PATH, else bail rather than assert nothing. - install.sh persists to ONE rc keyed off $SHELL, so install once per shell with SHELL=bash/zsh/fish, then assert a fresh INTERACTIVE shell of that kind (bash -ic / zsh -ic / fish -ic) — the one that reads that rc. - Per-shell negative control (bash/zsh): a fresh NON-interactive shell must NOT resolve the binary; if it does, the prefix leaked onto the base PATH and a pass would be meaningless. (Skipped for fish: fish_add_path writes a universal var and `fish -c` still sources config.fish.) Verified locally: with an isolated binary name (the dev host has a real tracebloc on PATH), a non-interactive shell does NOT resolve it while a fresh interactive shell does + `version` runs. bash -n, shellcheck (error + warning), actionlint all clean. The full distro × shell matrix is CI-only (real cli binary download + per-distro shell installs). Fixes #310. Follow-up to #214. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
divyasinghds
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes #310 (the remaining 🟠 item from @aptracebloc's #214 review; the Critical
guard()bug + CI-config gaps landed in #311). Reworkspath-persist.shso it can actually distinguish a fixed installer from a broken one.The problem
As merged in #214 the guard passed on a broken installer too:
install.shinstalled to/usr/local/bin(on every PATH). The installer only persists a PATH entry for an off-PATH prefix, so no rc was ever written and every shell resolved the binary trivially. No signal.login × non-loginmatrix mis-modelled "open a new terminal": a non-interactivebash -creads no rc file, and a login bash reads~/.profile— not the~/.bashrcthe installer writes on Linux.The fix
INSTALL_PREFIX=$HOME/.local/bin(a$HOMEdir the installer always persists, off the default root PATH), and precondition-check it isn't already on PATH — else bail rather than assert nothing.install.shpersists to a single rc keyed off$SHELL, so install once per shell withSHELL=bash/zsh/fish, then assert a fresh interactive shell of that kind (bash -ic/zsh -ic/fish -ic) — the one that reads that rc. The meaninglesslogindimension is dropped.fish_add_pathwrites a universal variable andfish -cstill sourcesconfig.fish; the global precondition covers fish).Test plan
traceblocon PATH): a fresh non-interactive shell does not resolve it, while a fresh interactive shell does andtracebloc versionruns — i.e. resolution is attributable to rc persistence.bash -n,shellcheck --severity=error(the gate) and--severity=warning(advisory),actionlint— all clean.distro × shellmatrix (real cli binary download + per-distro zsh/fish installs, interactive-shell behaviour without a tty across 9 base images) runs on GitHub — not claimed to pass locally.Fixes #310.
🤖 Generated with Claude Code
Note
Low Risk
Changes are limited to test scripts and workflow documentation; no production installer or runtime behavior is modified.
Overview
Reworks the PATH persist CI guard so it can fail on broken CLI installers instead of vacuously passing.
path-persist.shnow pinsINSTALL_PREFIX=$HOME/.local/binand aborts if that prefix is already onPATH, soinstall.shmust write rc persistence for the binary to be found. It downloadsinstall.shonce, then for each of bash/zsh/fish runs the installer withSHELLset to that shell and checks a fresh interactive shell (-ic) resolvestraceblocand runstracebloc version. The old login vs non-login matrix is removed. bash/zsh get a negative control: a non-interactive shell must not see the binary (detects prefix leaking onto basePATH).installer-tests.yamlcomments are updated to describe the off-PATH prefix, per-shell install, and interactive assertion model (references client#310).Reviewed by Cursor Bugbot for commit bf5090e. Bugbot is set up for automated code reviews on this repo. Configure here.