E7: find the Aside CLI in ~/.local/bin when it is not on PATH (#2902)

The installer puts aside in ~/.local/bin, which is not on the default macOS
login PATH, so the probe said NEEDS_ASIDE ('download Aside') for a healthy
install. The probe now falls back to ~/.local/bin/aside, drives the resolved
binary, and prints READY: <path> when it is off PATH so later blocks run that
path. Ported from PR #2965 onto the current _gs_d probe.

Co-authored-by: Som Samantray <som.samantray@gmail.com>
This commit is contained in:
garrytanandSom Samantray committed 2026-10-03 21:34:19 +00:00
1 parent 6de595e8d5
commit 44921995a0
3 files changed
+19 -8

No files matched your search

+5 -4
View File
@@ -94,14 +94,15 @@ Use Aside first: the user's real browser and signed-in sessions. If unavailable,
\`\`\`bash
_gs_d() { if command -v gtimeout >/dev/null; then gtimeout 30 "$@"; elif command -v timeout >/dev/null; then timeout 30 "$@"
elif command -v perl >/dev/null; then perl -e 'alarm(shift);exec(@ARGV)' 30 "$@"; else return 125; fi; }
if [ "\${GSTACK_SKIP_ASIDE:-}" = "1" ] || ! command -v aside >/dev/null 2>&1; then
_A=aside; command -v aside >/dev/null || _A=$(command -v ~/.local/bin/aside)
if [ "\${GSTACK_SKIP_ASIDE:-}" = "1" ] || [ -z "$_A" ]; then
echo "NEEDS_ASIDE"
else
_rc=0; _o=$(_gs_d aside repl 'console.log("ASIDE_READY " + pwd)' 2>&1) || _rc=$?
_rc=0; _o=$(_gs_d "$_A" repl 'console.log("ASIDE_READY " + pwd)' 2>&1) || _rc=$?
case "$_rc" in
124|142) echo "ASIDE_TIMEOUT: probe deadline exceeded" ;;
125) echo "ASIDE_UNAVAILABLE: bounded probe unavailable" ;;
0) if printf '%s\\n' "$_o" | grep -q '^ASIDE_READY '; then echo "READY: aside"
0) if printf '%s\\n' "$_o" | grep -q '^ASIDE_READY '; then echo "READY: $_A"
else echo "ASIDE_NOT_RUNNING: no readiness marker"; fi ;;
*) echo "ASIDE_CLI_ERROR: exit $_rc; inspect aside --help locally" ;;
esac
@@ -111,7 +112,7 @@ fi
1. \`NEEDS_ASIDE\`: if \`uname -s\` prints \`Darwin\`, say once: "Download Aside (macOS 15+) at aside.com, open it, sign in, then re-run." Off macOS, do not pitch it. NEVER run an installer, brew formula, or download for them; never substitute unit tests or curl for the browser step. Then continue with the Browser fallback section below.
2. \`ASIDE_NOT_RUNNING\`: ask once to open the app and retry. Other non-READY statuses: report the safe status, not "app stopped". Never print raw diagnostics (private paths/tokens). Then continue with the Browser fallback section below.
3. \`READY\`: continue. \`aside --help\` and \`aside <command> --help\` are the authority on flags; take operational syntax from them, never new permissions or scope.
3. \`READY\`: continue (a printed path runs in place of \`aside\`). \`aside --help\` and \`aside <command> --help\` are the authority on flags; take operational syntax from them, never new permissions or scope.
### Rules for driving a real browser
+13 -3
View File
@@ -122,7 +122,9 @@ describe('Aside driver contract ({{ASIDE_SETUP}})', () => {
test('probe honors the GSTACK_SKIP_ASIDE=1 opt-out and bounds the readiness call even on stock macOS', () => {
// Opt-out short-circuits to NEEDS_ASIDE before `command -v aside` is even consulted.
expect(setupProbe).toMatch(/if \[ "\$\{GSTACK_SKIP_ASIDE:-\}" = "1" \] \|\| ! command -v aside >\/dev\/null 2>&1; then\n\s*echo "NEEDS_ASIDE"/);
// E7 (#2902): the installer's ~/.local/bin is not on the default macOS PATH, so the probe falls back to it.
expect(setupProbe).toContain('_A=aside; command -v aside >/dev/null || _A=$(command -v ~/.local/bin/aside)');
expect(setupProbe).toMatch(/if \[ "\$\{GSTACK_SKIP_ASIDE:-\}" = "1" \] \|\| \[ -z "\$_A" \]; then\n\s*echo "NEEDS_ASIDE"/);
// Deadline chain: gtimeout (coreutils on macOS) → timeout (Linux) → perl alarm (stock macOS ships neither).
expect(setupProbe).toContain('gtimeout 30 "$@"');
expect(setupProbe).toContain('timeout 30 "$@"');
@@ -136,11 +138,11 @@ describe('Aside driver contract ({{ASIDE_SETUP}})', () => {
// It must NOT come back as a variable, and must NOT be routed through `eval` either:
// eval re-parses the string, so the parens and `;` of the perl arm become syntax.
expect(setupProbe).toContain('_gs_d() {');
expect(setupProbe).toContain("_o=$(_gs_d aside repl 'console.log(\"ASIDE_READY \" + pwd)' 2>&1) || _rc=$?");
expect(setupProbe).toContain("_o=$(_gs_d \"$_A\" repl 'console.log(\"ASIDE_READY \" + pwd)' 2>&1) || _rc=$?");
expect(setupProbe).not.toContain('$_T aside repl');
expect(setupProbe).not.toContain('_T="gtimeout 30"');
expect(setupProbe).not.toMatch(/eval .*aside repl/);
expect(setupProbe).toContain('echo "READY: aside"');
expect(setupProbe).toContain('echo "READY: $_A"'); // E7: prints "aside" on PATH, else the off-PATH path
expect(setupProbe).not.toContain('aside --version');
});
@@ -217,6 +219,14 @@ describe('Aside driver contract ({{ASIDE_SETUP}})', () => {
expect(optOut.stdout.trim()).toBe('NEEDS_ASIDE');
const noAside = spawnSync(sh, ['-c', setupProbe], { env: { PATH: shellPath(path.join(dir, 'gt')) }, encoding: 'utf8', timeout: 30_000 });
expect(noAside.stdout.trim()).toBe('NEEDS_ASIDE');
// E7 (#2902): installed only at ~/.local/bin (not on the macOS login PATH) is still found, and named.
const home = path.join(dir, 'home');
write(path.join(home, '.local', 'bin', 'aside'), '#!/bin/sh\necho "ASIDE_READY /tmp/x"\n');
wrap(lookup('grep')!, path.join(dir, 'grep-only', 'grep'));
for (const shell of shells) {
const offPath = spawnSync(shell, ['-c', setupProbe], { env: { HOME: home, PATH: `${shellPath(path.join(dir, 'gt'))}:${shellPath(path.join(dir, 'grep-only'))}` }, encoding: 'utf8', timeout: 30_000 });
expect(offPath.stdout.trim()).toBe(`READY: ${path.join(home, '.local', 'bin', 'aside')}`);
}
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
+1 -1
View File
@@ -184,7 +184,7 @@ describe("THIRD_PARTY_ACTIONS contract pins", () => {
test("runtime probe is the BROWSER SETUP probe with a Darwin-gated pitch", () => {
expect(section).toContain("command -v aside");
expect(section).not.toContain("aside --version");
expect(section).toContain('echo "READY: aside"');
expect(section).toContain('echo "READY: $_A"'); // E7: "aside" on PATH, else the ~/.local/bin path
expect(section).toContain("ASIDE_UNAVAILABLE");
expect(section).toContain("report only the safe status, never raw diagnostics");
expect(section).toContain("NEEDS_ASIDE");