Skip to content

Commit 12c0b89

Browse files
committed
fix(setup): Playwright Chromium failure becomes best-effort warn, not exit 1
Replaces both `exit 1` paths in the Playwright install/verify block with a $_PW_FAIL_REASON accumulator that prints a named warning to stderr and returns control. Previously a Chromium download failure (network blip, locked-down corporate machine, missing Node.js on Windows) would abort ./setup mid-run with no skills registered, no hooks installed, no migrations applied — and a re-run would start over from the top. Now the failure only disables browser-driven features (/qa, /design-review, make-pdf, sidebar, /pair-agent); skills, hooks, and migrations land normally. The warning names which sub-step failed (chromium-install / windows-no-node / windows-node-modules / post-install-launch) so users and bug reports can be specific, and tells the user to re-run ./setup to retry just this step. Tests (5) exercise the actual block from setup with stubbed ensure_playwright_browser and bunx, simulate install + post-install failures, and assert the script exits 0 with the named warning. One side-by-side test inlines the OLD bug shape and confirms it returned non-zero, so the exit-code difference is proven, not just asserted. Coordinates with garrytan#1838 (pins Playwright install version): both touch the same code block, so whichever lands first needs the other to rebase. The changes are complementary — garrytan#1838 fixes which Chromium build gets installed; this PR fixes what happens when the install fails. Carved out from garrytan#1883 per @jbetala7's review request.
1 parent d8c91c6 commit 12c0b89

5 files changed

Lines changed: 301 additions & 99 deletions

File tree

CHANGELOG.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,89 @@
11
# Changelog
22

3+
## [1.57.4.0] - 2026-06-08
4+
5+
## **A failed Playwright Chromium install no longer aborts `./setup` mid-run. Skills, hooks, and migrations are already in place; you get a named warning and a retry hint.**
6+
7+
The Playwright Chromium install is the riskiest step in `./setup`. It needs
8+
network, it talks to a Microsoft CDN, on Windows it bounces through Node.js
9+
because of a Bun pipe bug, and on locked-down machines it may not even be
10+
allowed to run. Until now, any failure in that step was fatal: `exit 1`, and
11+
the run dies before skills get registered, before hooks land, before
12+
migrations run. Re-running `./setup` would start over from the top.
13+
14+
That's the wrong shape. The user already has a working bun, a built browse
15+
binary, and a tree of skill templates ready to go. The thing that failed —
16+
Chromium availability — only affects browser-driven features (`/qa`,
17+
`/design-review`, `make-pdf`, sidebar, `/pair-agent`). Everything else can
18+
still work. So make the failure non-fatal.
19+
20+
This release replaces both `exit 1` paths in the Playwright install/verify
21+
block with a `$_PW_FAIL_REASON` accumulator. On install failure the script
22+
prints a named warning telling you which sub-step failed, names the
23+
affected features, gives Windows users the specific known-issue link, and
24+
tells you to re-run `./setup` to retry — at which point only the Playwright
25+
step needs to succeed, because everything else is already done.
26+
27+
### The numbers that matter
28+
29+
Source: `bun test test/setup-playwright-warn-not-exit.test.ts` on this branch.
30+
31+
| Metric | Before | After | Δ |
32+
|--------|--------|-------|---|
33+
| Playwright failure aborts `./setup` | yes | no | warn instead of exit |
34+
| Skills registered when Chromium download fails | sometimes | always | depends on which step failed first |
35+
| Named failure reason in warning | no | yes | `chromium-install`, `windows-no-node`, `windows-node-modules`, `post-install-launch` |
36+
| Retry guidance in failure output | no | yes | "Re-run ./setup to retry" |
37+
38+
The four named reasons matter. When a Playwright install fails it's usually
39+
not obvious whether the download died, Chromium was downloaded but won't
40+
launch, or Node.js isn't on PATH (Windows). The warning calls it by name
41+
so users and bug reports can be specific.
42+
43+
### What this means for you
44+
45+
If your network blinks during `./setup`, or you're on a corporate machine
46+
that won't let `bunx playwright install chromium` finish, you'll now get
47+
gstack installed with everything except browser features, plus a clear
48+
warning. Re-run `./setup` when the network is back to enable browser
49+
features. If you're on Windows and hit the Bun-on-Windows pipe bug, the
50+
warning tells you exactly which `node -e` check to verify.
51+
52+
### Itemized changes
53+
54+
#### Changed
55+
56+
- `setup`: Playwright Chromium install/verify no longer aborts on failure.
57+
The two `exit 1` paths in the install block are replaced with a
58+
`$_PW_FAIL_REASON` accumulator (values: `chromium-install`,
59+
`windows-no-node`, `windows-node-modules`, `post-install-launch`). When
60+
set, the script prints a named warning to stderr, lists the affected
61+
browser-driven features, includes the Bun-on-Windows known-issue link
62+
if applicable, and tells the user to re-run `./setup` to retry. All
63+
other setup steps complete normally.
64+
65+
#### For contributors
66+
67+
- `test/setup-playwright-warn-not-exit.test.ts` — 5 tests:
68+
- Two source-anchored checks confirm no `exit 1` is reachable from the
69+
Playwright block and the `_PW_FAIL_REASON` accumulator pattern is in
70+
place.
71+
- Two functional tests execute the live block from `setup` with stubbed
72+
`ensure_playwright_browser` and `bunx`, simulate install + post-install
73+
failures, and assert the script exits 0 with the named warning.
74+
- One side-by-side test inlines the OLD `exit 1` bug shape and confirms
75+
it returned non-zero — proves the exit-code difference is real, not a
76+
quoting illusion.
77+
78+
#### Coordination
79+
80+
- This PR is one of three carved out from #1883 per @jbetala7's review
81+
request. It touches the same code block as #1838 (which pins the
82+
Playwright install version), so whichever of #1838 or this PR lands
83+
first will need a small rebase on the other. The two changes are
84+
complementary — #1838 fixes which Chromium build gets installed; this
85+
PR fixes what happens when the install fails.
86+
387
## [1.57.3.0] - 2026-06-07
488

589
## **Every PR `/ship` opens gets the version stamped into its title, fork and agent PRs included.**

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.57.3.0
1+
1.57.4.0

package.json

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,74 @@
11
{
2-
"name": "gstack",
3-
"version": "1.57.3.0",
4-
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
5-
"license": "MIT",
6-
"type": "module",
7-
"bin": {
8-
"browse": "./browse/dist/browse",
9-
"make-pdf": "./make-pdf/dist/pdf"
10-
},
11-
"scripts": {
12-
"build": "bash scripts/build.sh",
13-
"vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js",
14-
"dev:make-pdf": "bun run make-pdf/src/cli.ts",
15-
"dev:design": "bun run design/src/cli.ts",
16-
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
17-
"gen:skill-docs:user": "bun run scripts/gen-skill-docs.ts --respect-detection",
18-
"dev": "bun run browse/src/cli.ts",
19-
"server": "bun run browse/src/server.ts",
20-
"test": "bun test browse/test/ test/ make-pdf/test/ --ignore 'test/skill-e2e-*.test.ts' --ignore test/skill-llm-eval.test.ts --ignore test/skill-routing-e2e.test.ts --ignore test/codex-e2e.test.ts --ignore test/gemini-e2e.test.ts && (bun run slop:diff 2>/dev/null || true)",
21-
"test:free": "bun run scripts/test-free-shards.ts",
22-
"test:windows": "bun run scripts/test-free-shards.ts --windows-only",
23-
"test:evals": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
24-
"test:evals:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
25-
"test:e2e": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
26-
"test:e2e:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
27-
"test:gate": "EVALS=1 EVALS_TIER=gate bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
28-
"test:periodic": "EVALS=1 EVALS_TIER=periodic EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
29-
"test:codex": "EVALS=1 bun test test/codex-e2e.test.ts",
30-
"test:codex:all": "EVALS=1 EVALS_ALL=1 bun test test/codex-e2e.test.ts",
31-
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
32-
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
33-
"skill:check": "bun run scripts/skill-check.ts",
34-
"dev:skill": "bun run scripts/dev-skill.ts",
35-
"start": "bun run browse/src/server.ts",
36-
"eval:list": "bun run scripts/eval-list.ts",
37-
"eval:compare": "bun run scripts/eval-compare.ts",
38-
"eval:summary": "bun run scripts/eval-summary.ts",
39-
"eval:watch": "bun run scripts/eval-watch.ts",
40-
"eval:select": "bun run scripts/eval-select.ts",
41-
"analytics": "bun run scripts/analytics.ts",
42-
"test:audit": "bun test test/audit-compliance.test.ts",
43-
"slop": "npx slop-scan scan . 2>/dev/null || echo 'slop-scan not available (install with: npm i -g slop-scan)'",
44-
"slop:diff": "bun run scripts/slop-diff.ts"
45-
},
46-
"dependencies": {
47-
"@huggingface/transformers": "^4.1.0",
48-
"@ngrok/ngrok": "^1.7.0",
49-
"diff": "^7.0.0",
50-
"marked": "^18.0.2",
51-
"playwright": "^1.58.2",
52-
"puppeteer-core": "^24.40.0",
53-
"socks": "^2.8.8"
54-
},
55-
"engines": {
56-
"bun": ">=1.0.0"
57-
},
58-
"keywords": [
59-
"browser",
60-
"automation",
61-
"playwright",
62-
"headless",
63-
"cli",
64-
"claude",
65-
"ai-agent",
66-
"devtools"
67-
],
68-
"devDependencies": {
69-
"@anthropic-ai/claude-agent-sdk": "0.2.117",
70-
"@anthropic-ai/sdk": "^0.78.0",
71-
"xterm": "5",
72-
"xterm-addon-fit": "^0.8.0"
73-
}
2+
"name": "gstack",
3+
"version": "1.57.4.0",
4+
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
5+
"license": "MIT",
6+
"type": "module",
7+
"bin": {
8+
"browse": "./browse/dist/browse",
9+
"make-pdf": "./make-pdf/dist/pdf"
10+
},
11+
"scripts": {
12+
"build": "bash scripts/build.sh",
13+
"vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js",
14+
"dev:make-pdf": "bun run make-pdf/src/cli.ts",
15+
"dev:design": "bun run design/src/cli.ts",
16+
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
17+
"gen:skill-docs:user": "bun run scripts/gen-skill-docs.ts --respect-detection",
18+
"dev": "bun run browse/src/cli.ts",
19+
"server": "bun run browse/src/server.ts",
20+
"test": "bun test browse/test/ test/ make-pdf/test/ --ignore 'test/skill-e2e-*.test.ts' --ignore test/skill-llm-eval.test.ts --ignore test/skill-routing-e2e.test.ts --ignore test/codex-e2e.test.ts --ignore test/gemini-e2e.test.ts && (bun run slop:diff 2>/dev/null || true)",
21+
"test:free": "bun run scripts/test-free-shards.ts",
22+
"test:windows": "bun run scripts/test-free-shards.ts --windows-only",
23+
"test:evals": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
24+
"test:evals:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
25+
"test:e2e": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
26+
"test:e2e:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
27+
"test:gate": "EVALS=1 EVALS_TIER=gate bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
28+
"test:periodic": "EVALS=1 EVALS_TIER=periodic EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
29+
"test:codex": "EVALS=1 bun test test/codex-e2e.test.ts",
30+
"test:codex:all": "EVALS=1 EVALS_ALL=1 bun test test/codex-e2e.test.ts",
31+
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
32+
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
33+
"skill:check": "bun run scripts/skill-check.ts",
34+
"dev:skill": "bun run scripts/dev-skill.ts",
35+
"start": "bun run browse/src/server.ts",
36+
"eval:list": "bun run scripts/eval-list.ts",
37+
"eval:compare": "bun run scripts/eval-compare.ts",
38+
"eval:summary": "bun run scripts/eval-summary.ts",
39+
"eval:watch": "bun run scripts/eval-watch.ts",
40+
"eval:select": "bun run scripts/eval-select.ts",
41+
"analytics": "bun run scripts/analytics.ts",
42+
"test:audit": "bun test test/audit-compliance.test.ts",
43+
"slop": "npx slop-scan scan . 2>/dev/null || echo 'slop-scan not available (install with: npm i -g slop-scan)'",
44+
"slop:diff": "bun run scripts/slop-diff.ts"
45+
},
46+
"dependencies": {
47+
"@huggingface/transformers": "^4.1.0",
48+
"@ngrok/ngrok": "^1.7.0",
49+
"diff": "^7.0.0",
50+
"marked": "^18.0.2",
51+
"playwright": "^1.58.2",
52+
"puppeteer-core": "^24.40.0",
53+
"socks": "^2.8.8"
54+
},
55+
"engines": {
56+
"bun": ">=1.0.0"
57+
},
58+
"keywords": [
59+
"browser",
60+
"automation",
61+
"playwright",
62+
"headless",
63+
"cli",
64+
"claude",
65+
"ai-agent",
66+
"devtools"
67+
],
68+
"devDependencies": {
69+
"@anthropic-ai/claude-agent-sdk": "0.2.117",
70+
"@anthropic-ai/sdk": "^0.78.0",
71+
"xterm": "5",
72+
"xterm-addon-fit": "^0.8.0"
73+
}
7474
}

setup

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -475,44 +475,57 @@ if [ "$INSTALL_OPENCODE" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then
475475
)
476476
fi
477477

478-
# 2. Ensure Playwright's Chromium is available
478+
# 2. Ensure Playwright's Chromium is available (best-effort: warn on failure).
479+
#
480+
# When Chromium download or launch fails (network issue, missing sudo,
481+
# offline mirror, Bun-on-Windows pipe bug), browser-driven features
482+
# (/qa, /design-review, make-pdf, sidebar, /pair-agent) are unavailable
483+
# until the user retries. All other gstack functionality (skill
484+
# registration, hooks, migrations) is already installed by earlier steps.
485+
# Previously this step was fatal (exit 1) and would abort the whole run
486+
# mid-setup, leaving skills unregistered. Now it sets $_PW_FAIL_REASON,
487+
# prints a named warning, and returns control. Re-running ./setup retries.
488+
_PW_FAIL_REASON=""
489+
479490
if ! ensure_playwright_browser; then
480491
echo "Installing Playwright Chromium..."
481-
(
482-
cd "$SOURCE_GSTACK_DIR"
483-
bunx playwright install chromium
484-
)
492+
if ! ( cd "$SOURCE_GSTACK_DIR" && bunx playwright install chromium ); then
493+
_PW_FAIL_REASON="chromium-install"
494+
fi
485495

486-
if [ "$IS_WINDOWS" -eq 1 ]; then
496+
if [ -z "$_PW_FAIL_REASON" ] && [ "$IS_WINDOWS" -eq 1 ]; then
487497
# On Windows, Node.js launches Chromium (not Bun — see oven-sh/bun#4253).
488498
# Ensure playwright is importable by Node from the gstack directory.
489499
if ! command -v node >/dev/null 2>&1; then
490-
echo "gstack setup failed: Node.js is required on Windows (Bun cannot launch Chromium due to a pipe bug)" >&2
491-
echo " Install Node.js: https://nodejs.org/" >&2
492-
exit 1
500+
_PW_FAIL_REASON="windows-no-node"
501+
else
502+
echo "Windows detected — verifying Node.js can load Playwright..."
503+
if ! ( cd "$SOURCE_GSTACK_DIR" && \
504+
# Bun's node_modules already has playwright; verify Node can require it
505+
{ node -e "require('playwright')" 2>/dev/null || npm install --no-save playwright; } && \
506+
# @ngrok/ngrok is externalized in server-node.mjs and resolved at runtime.
507+
# Verify the platform-specific native binary is installed so /pair-agent
508+
# tunnels don't fail later with a cryptic module-not-found error.
509+
{ node -e "require('@ngrok/ngrok')" 2>/dev/null || npm install --no-save @ngrok/ngrok; } ); then
510+
_PW_FAIL_REASON="windows-node-modules"
511+
fi
493512
fi
494-
echo "Windows detected — verifying Node.js can load Playwright..."
495-
(
496-
cd "$SOURCE_GSTACK_DIR"
497-
# Bun's node_modules already has playwright; verify Node can require it
498-
node -e "require('playwright')" 2>/dev/null || npm install --no-save playwright
499-
# @ngrok/ngrok is externalized in server-node.mjs and resolved at runtime.
500-
# Verify the platform-specific native binary is installed so /pair-agent
501-
# tunnels don't fail later with a cryptic module-not-found error.
502-
node -e "require('@ngrok/ngrok')" 2>/dev/null || npm install --no-save @ngrok/ngrok
503-
)
504513
fi
505514
fi
506515

507-
if ! ensure_playwright_browser; then
516+
if [ -z "$_PW_FAIL_REASON" ] && ! ensure_playwright_browser; then
517+
_PW_FAIL_REASON="post-install-launch"
518+
fi
519+
520+
if [ -n "$_PW_FAIL_REASON" ]; then
521+
echo "" >&2
522+
echo " warning: Playwright Chromium is unavailable ($_PW_FAIL_REASON)." >&2
523+
echo " Browser features (/qa, /design-review, make-pdf, sidebar, /pair-agent) are unavailable." >&2
508524
if [ "$IS_WINDOWS" -eq 1 ]; then
509-
echo "gstack setup failed: Playwright Chromium could not be launched via Node.js" >&2
510-
echo " This is a known issue with Bun on Windows (oven-sh/bun#4253)." >&2
511-
echo " Ensure Node.js is installed and 'node -e \"require('playwright')\"' works." >&2
512-
else
513-
echo "gstack setup failed: Playwright Chromium could not be launched" >&2
525+
echo " Known Bun-on-Windows issue (oven-sh/bun#4253). Ensure Node.js is installed," >&2
526+
echo " then verify: node -e \"require('playwright')\"" >&2
514527
fi
515-
exit 1
528+
echo " Re-run ./setup to retry. Skills, hooks, and migrations are already installed." >&2
516529
fi
517530

518531
# 2b. Ensure a color-emoji font is installed so make-pdf emoji render (Linux).

0 commit comments

Comments
 (0)