* fix(#3020): probe user shell PATH at install-time, not just process.env.PATH
The installer's "✓ GSD SDK ready" message was a false positive whenever
the install subprocess's process.env.PATH contained the gsd-sdk shim
but the user's later interactive shells did not. Three known sources
of mismatch on POSIX:
- ~/.local/bin: install subprocess inherits npm/npx-injected PATH;
user's login shell may not add ~/.local/bin if .profile/.bashrc/
.zshrc don't.
- nvm/fnm/volta: node version managers shim PATH per-shell, so
`npm prefix -g` from inside the install subprocess can resolve to
a different bin dir than the user's interactive shell sees.
- npm-prefix tooling: some installers inject extra PATH entries that
vanish in fresh sessions.
Result reported on #3011 by @x0rk and @stefanoginella: install prints
✓, but every workflow invocation later fails with
"bash: gsd-sdk: command not found".
Fix:
- isGsdSdkOnPath(pathString?) — now accepts an explicit PATH string.
Zero-arg form preserves existing behavior (reads process.env.PATH).
Pure walk, no spawn. Lets callers verify against any PATH source.
- getUserShellPath() — new helper. Probes the user's login shell via
`$SHELL -lc 'printf %s "$PATH"'` (POSIX). 2-second timeout so a
misconfigured rc file can't hang the install. Returns null on
Windows (cross-shell PATH probing requires a different strategy
per Git Bash / PowerShell / cmd.exe — tracked separately) or when
the probe fails; callers fall back to process.env.PATH in that case.
- installSdkIfNeeded() — after the existing isGsdSdkOnPath() check
passes, also verify the shim is reachable from getUserShellPath()
on POSIX. If install-PATH and user-shell-PATH disagree, downgrade
to the actionable ⚠ diagnostic from PR #3014 (which has the shim
location, shell-specific PATH-update commands, and an npx fallback
note). Routing affected users into PR #3014's diagnostic is the
point — not silently green-then-red.
Tests:
- bug-3020-install-shell-path-probe.test.cjs (10 tests, structural):
- isGsdSdkOnPath accepts an explicit PATH (true/false on fixture
PATH dirs with/without an executable shim)
- zero-arg form returns a boolean
- empty string PATH → false
- getUserShellPath returns string-or-null
- returns null on Windows
- returns null when $SHELL unset on POSIX
- cross-shell mismatch detection: install-PATH and user-PATH that
differ produce different isGsdSdkOnPath results — the invariant
the install-time check now exploits
- All assertions on structural records, not console output. Adheres
to typed-IR / CONTRIBUTING.md "Prohibited: Raw Text Matching".
Verification:
- 10/10 pass on new regression test
- 6768/6768 pass on full suite (5 net-new tests)
- lint-no-source-grep clean
Windows cross-shell coverage (gsd-sdk.cmd resolves under PowerShell
but not Git Bash without a no-extension sibling) is tracked separately
— this PR is the POSIX-side fix and the Windows scaffolding (the
optional pathString arg on isGsdSdkOnPath) that a Windows fix can
build on.
Closes#3020
* fix(#3020): type-guard pathString, last-line PATH parse (CR)
CodeRabbit on PR #3028 (4 findings — 3 actionable + 1 nitpick):
1. .changeset/install-shell-path-probe.md (2 findings):
- `pr: TBD` → `pr: 3028`
- Doc said `echo $PATH` but impl uses `printf %s "$PATH"` (chosen
to avoid shell-dependent echo behavior, e.g. interpreting `-n`).
Aligned changeset prose with implementation.
2. bin/install.js:9176 — isGsdSdkOnPath(pathString) used
`pathString !== undefined` to gate the explicit-PATH branch, but
getUserShellPath() can return null and `null.split()` throws.
Tightened to `typeof pathString === 'string'` so null / number /
object inputs fall back to process.env.PATH. Added 2 regression
tests covering the null and non-string cases.
3. bin/install.js:9232 — getUserShellPath trimmed entire stdout. A
misconfigured rc file that prints a banner / motd / log line
BEFORE the printf would pollute the result and incorrectly flip
the cross-shell check to false. Take the LAST non-empty line
(PATH itself is single-line) so noise can't hijack the probe.
4. Nitpick: the changeset PR placeholder — covered by (1).
Verification: 12/12 pass on regression test (10 original + 2 new
type-guard tests), 6770/6770 full suite, lint clean.
* docs(#3020): JSDoc references printf %s "$PATH", not echo $PATH (CR)
CodeRabbit caught two stale JSDoc references that still said
`$SHELL -lc 'echo $PATH'` while the implementation uses
`$SHELL -lc 'printf %s "$PATH"'`. echo is undesirable here because:
- POSIX echo's behavior with `-n` / backslash escapes varies across
shells (bash builtin vs /bin/echo vs zsh) and can introduce
trailing-newline pollution that the per-line trim now papers over.
- printf is portable and emits exactly the bytes given.
Synced both stale doc strings:
- bin/install.js:9211 (getUserShellPath JSDoc)
- tests/bug-3020-install-shell-path-probe.test.cjs:27 (header)
No behavior change — implementation already uses printf.