Fix CI: relative img path in /visual-mode + sandbox flags for Puppeteer

Two unrelated breakages were stacking on the v2.0 PR:

1. The static site build crashed because the generated /visual-mode page
   referenced images via root-absolute paths (/antipattern-images/*.png).
   Bun's HTML loader resolves <img src> at build time relative to the
   source HTML file and treats a leading slash as filesystem-absolute, so
   it could not find the images. Use a relative path so Bun bundles and
   hashes them the same way the homepage already does.

2. The Puppeteer-backed fixture tests crashed in GitHub Actions because
   the Ubuntu runners block unprivileged user namespaces, so Chrome's
   sandbox cannot initialize. Pass --no-sandbox / --disable-setuid-sandbox
   only when process.env.CI is set, so local users keep the hardened
   default launch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-08 13:57:41 -07:00
co-authored by Claude Opus 4.6
parent f1d4131964
commit a6a58f712c
2 changed files with 6 additions and 2 deletions
+1 -1
View File
@@ -450,7 +450,7 @@ function renderVisualModeMain() {
(item) => `
<a class="gallery-card" href="/antipattern-examples/${item.id}.html">
<div class="gallery-card-thumb">
<img src="/antipattern-images/${item.id}.png" alt="${escapeAttr(item.title)} specimen" loading="lazy" width="540" height="540">
<img src="../antipattern-images/${item.id}.png" alt="${escapeAttr(item.title)} specimen" loading="lazy" width="540" height="540">
</div>
<div class="gallery-card-body">
<h3 class="gallery-card-title">${escapeHtml(item.title)}</h3>
+5 -1
View File
@@ -2695,7 +2695,11 @@ async function detectUrl(url) {
throw new Error(`Browser script not found at ${browserScriptPath}`);
}
const browser = await puppeteer.default.launch({ headless: true });
// CI runners (GitHub Actions Ubuntu) block unprivileged user namespaces, so
// Chrome can't initialize its sandbox there. Disable the sandbox only when
// running in CI; local users keep the default hardened launch.
const launchArgs = process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [];
const browser = await puppeteer.default.launch({ headless: true, args: launchArgs });
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });