Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 65 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ jobs:
name: bun (lint + typecheck + test)
# Blacksmith 2 vCPU runner — fast on small matrices, plenty for this suite.
runs-on: blacksmith-2vcpu-ubuntu-2204
timeout-minutes: 10
# 10 was too tight (run 34577257568 was cancelled at 10m17s mid-install).
# 20 now exists for the retry's sake, not to accommodate a hang: three
# bounded attempts (3 × 300s) + backoff (20s + 40s) + ~30s for the rest of
# the job ≈ 16.5m, so every attempt gets its full budget inside the ceiling.
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

Expand All @@ -41,21 +45,74 @@ jobs:
# Browser integration tests drive a real Chromium. Cache the Playwright-
# managed browser (keyed on bun.lock so a playwright-core bump invalidates it)
# so it isn't re-downloaded each run; the adapter prefers managed Chromium.
# v2 because the v1 entry is incomplete and immutable: it holds
# chromium-1228 but not chromium_headless_shell-1228, which every headless
# launch needs (run 34583721201). v2 lets the first run save a complete
# set; the prefix restore-key still falls back to v1.
- name: cache Playwright Chromium
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('bun.lock') }}
key: ${{ runner.os }}-playwright-v2-${{ hashFiles('bun.lock') }}
restore-keys: ${{ runner.os }}-playwright-

# No apt on any path. The install fetches the browser binaries from
# Playwright's CDN over HTTPS, or fills in what the cache above is missing
# — it never touches the Ubuntu mirrors, whose port-80 egress from these
# runners is what actually flakes (run 34580929047: one apt attempt
# crawled 11m before failing, the second was still crawling when the 20m
# ceiling killed the job).
# The system libraries `install-deps` would add are already on the stock
# image: apt's own plan in that run listed all 14 runtime libs (libnss3,
# libgbm1, libasound2, libcups2, …) as UPGRADES, not new installs, and the
# only NEW packages were fonts — which affect glyph rendering in
# screenshots, and no CI assertion reads rendered output. The launch check
# in the next step gates that claim at runtime.
# The CLI is the repo-pinned playwright-core's own — NOT `bunx playwright`,
# which resolves an unpinned version: run 34583721201 had it exit 0 in
# 0.7s without fetching the headless shell the pinned version requires.
# Each attempt is bounded: `timeout` kills a wedged or crawling attempt so
# the retry actually fires. Exit 124 (or 137 after the KILL fallback) means
# the budget was exceeded — logged as such and treated as a failed attempt.
# 300s dwarfs the ~19s a healthy install took while leaving 3 attempts +
# backoff inside timeout-minutes (a cold full fetch is ~300 MB).
- name: install chromium
run: |
if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then
bunx playwright install-deps chromium
else
bunx playwright install --with-deps chromium
fi
for attempt in 1 2 3; do
timeout --kill-after=30 300 node node_modules/playwright-core/cli.js install chromium
rc=$?
if [ "$rc" -eq 0 ]; then
echo "chromium installed (attempt $attempt/3)"
exit 0
fi
if [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ]; then
echo "::warning::attempt $attempt/3 exceeded its 300s budget and was killed (exit $rc)"
else
echo "::warning::chromium install attempt $attempt/3 failed (exit $rc)"
fi
if [ "$attempt" -lt 3 ]; then
sleep $((attempt * 20))
fi
done

echo "::error::chromium install failed after 3 attempts — see output above"
exit 1

# Runtime gate: prove the launch surface the suite needs is actually
# there — both the system libraries (the no-apt claim above) and the
# browser set (this is what caught run 34583721201: the cached browser
# lacked chrome-headless-shell, which headless launches — the suite's
# launchPersistentContext included — resolve to). If anything is missing,
# this fails HERE with the error naming it, not as a mystery inside
# `bun test`. Not retried: a missing binary or library is not transient.
- name: verify chromium launches
run: |
bun -e '(async () => {
const { chromium } = await import("playwright-core");
const browser = await chromium.launch({ headless: true });
console.log("chromium " + browser.version() + " launched headless, no apt deps");
await browser.close();
})().catch((err) => { console.error(err); process.exit(1); })'

# bun test runs test files in parallel by default. The low-level BrowserAdapter
# integration suite flakes under the runner's parallel 2-vCPU load (its own
Expand Down
Loading