From 29d94a505d83c6a438adc0f8b266080f68af3ccb Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sat, 29 Aug 2026 04:36:38 +0000 Subject: [PATCH] fix(ci): free-tests lane actually runs the make-pdf e2e gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 9 make-pdf/test/e2e gate tests probe make-pdf/dist/pdf, browse/dist/browse, and the diagram-render bundle, then self-skip when absent. The required free-tests lane never built any of them, so the gates silently skipped on Linux for their entire life (verified: 9 of 14 skip, exit 0). make-pdf-gate.yml's justification for deleting its Linux leg claimed the free lane covered this — it didn't. - new build:gates script: exactly the three artifacts the gates probe (full bun run build compiles five binaries; ~60-90s tax on the only required check is not warranted) - free-tests.yml: build:gates step + poppler-utils + fonts-noto-color-emoji (fonts must precede the first browse daemon launch — Chromium snapshots fontconfig at startup; verified live: a warm daemon renders tofu, a fresh one embeds NotoColorEmoji) - make-pdf/test/e2e/ci-prereqs.test.ts: GSTACK_EXPECT_BINARIES=1 (set by the workflow) inverts the skip polarity in CI — dropping the build step or poppler fails the lane instead of re-opening the silent-skip hole Pre-flight: all 9 gates green on Linux locally. Co-Authored-By: Claude Fable 5 --- .github/workflows/free-tests.yml | 25 ++++++++++++++-- .github/workflows/make-pdf-gate.yml | 10 ++++--- make-pdf/test/e2e/ci-prereqs.test.ts | 44 ++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 make-pdf/test/e2e/ci-prereqs.test.ts diff --git a/.github/workflows/free-tests.yml b/.github/workflows/free-tests.yml index 782727772..76b579d27 100644 --- a/.github/workflows/free-tests.yml +++ b/.github/workflows/free-tests.yml @@ -82,9 +82,14 @@ jobs: # Headed-browser tests (handoff, extension sidepanel DOM) need a real # DISPLAY — first Linux run failed with Playwright's "launched a headed # browser without an XServer" banner. xvfb-run below provides it; - # x11-utils ships xdpyinfo for display probing. - - name: Install Xvfb + X11 utilities - run: sudo apt-get install -y --no-install-recommends xvfb x11-utils + # x11-utils ships xdpyinfo for display probing. poppler-utils ships + # pdftotext/pdffonts/pdftoppm for the make-pdf e2e gates; + # fonts-noto-color-emoji is the emoji-gate's render font (playwright + # --with-deps usually installs it, but the gate must not depend on a + # transitive package list). Fonts must land BEFORE the first browse + # daemon launch — Chromium snapshots fontconfig at startup. + - name: Install Xvfb + X11 utilities + gate tools + run: sudo apt-get install -y --no-install-recommends xvfb x11-utils poppler-utils fonts-noto-color-emoji - name: Configure git identity (tests init temp repos) run: | @@ -108,8 +113,22 @@ jobs: - name: Build server-node bundle (loaded by browse cli imports) run: bash browse/scripts/build-node-server.sh + # Narrowed gate build: the make-pdf e2e gates probe make-pdf/dist/pdf, + # browse/dist/browse, and the diagram-render bundle, then self-skip when + # absent — which made them silently skip on Linux for their whole life + # (this lane never built binaries). Full `bun run build` compiles five + # binaries and would add ~60-90s to the ONLY required check; the gates + # need exactly these three artifacts. + - name: Build gate binaries (make-pdf e2e gates) + run: bun run build:gates + + # GSTACK_EXPECT_BINARIES=1 arms make-pdf/test/e2e/ci-prereqs.test.ts: + # if a future edit drops the gate build (or poppler), the lane FAILS + # instead of the gates silently self-skipping back to false green. - name: Run free suite run: xvfb-run -a bun run test:free + env: + GSTACK_EXPECT_BINARIES: "1" # The runner streams the full child output to per-run logs under the OS # tmpdir and prints only the quiet contract to the console. Without this diff --git a/.github/workflows/make-pdf-gate.yml b/.github/workflows/make-pdf-gate.yml index fa9808276..1259a951e 100644 --- a/.github/workflows/make-pdf-gate.yml +++ b/.github/workflows/make-pdf-gate.yml @@ -24,10 +24,12 @@ jobs: strategy: fail-fast: false matrix: - # macOS only: the Linux leg became redundant when the free-tests lane - # started running make-pdf/test (incl. e2e/) on every PR via the - # canonical runner — this gate's remaining value is macOS rendering - # coverage on make-pdf-scoped changes. + # macOS only: the Linux leg is covered by the free-tests lane, which + # builds the gate binaries (build:gates) and runs make-pdf/test + # (incl. e2e/) on every PR via the canonical runner, with + # GSTACK_EXPECT_BINARIES=1 arming ci-prereqs.test.ts so the gates + # can never silently self-skip there again. This gate's remaining + # value is macOS rendering coverage on make-pdf-scoped changes. os: [macos-latest] # Windows is tolerant-mode — Xpdf / Poppler-Windows extraction # differs enough from the Linux/macOS baseline that the strict diff --git a/make-pdf/test/e2e/ci-prereqs.test.ts b/make-pdf/test/e2e/ci-prereqs.test.ts new file mode 100644 index 000000000..e5618c8ff --- /dev/null +++ b/make-pdf/test/e2e/ci-prereqs.test.ts @@ -0,0 +1,44 @@ +/** + * CI tripwire for the silent-skip class (#audit-2026-08: the 9 make-pdf e2e + * gate tests self-skipped on Linux for their entire life because the + * free-tests lane never built the binaries they probe — exit 0, no signal). + * + * Every sibling gate file guards itself with test.skipIf(!prerequisitesAvailable()), + * which is correct for LOCAL runs (a contributor without a build shouldn't + * fail) but is exactly how CI green stopped meaning "ran". This file inverts + * the polarity in CI: when GSTACK_EXPECT_BINARIES=1 (set by free-tests.yml's + * "Run free suite" step), the prerequisites are ASSERTED, so dropping the + * gate-build step or poppler from the workflow fails the required lane + * instead of quietly skipping the gates. + * + * Not set locally → the whole file self-skips, same as the gates. + */ +import { describe, expect, test } from "bun:test"; +import * as fs from "node:fs"; +import * as path from "node:path"; + +import { resolvePdftotext } from "../../src/pdftotext"; + +const ROOT = path.resolve(__dirname, "../../.."); +const EXPECT_BINARIES = process.env.GSTACK_EXPECT_BINARIES === "1"; + +describe("gate prerequisites (CI tripwire)", () => { + test.skipIf(!EXPECT_BINARIES)("gate artifacts and tools exist when the lane promises them", () => { + const missing: string[] = []; + for (const rel of [ + "make-pdf/dist/pdf", + "browse/dist/browse", + "lib/diagram-render/dist/diagram-render.html", + ]) { + if (!fs.existsSync(path.join(ROOT, rel))) missing.push(rel); + } + try { + resolvePdftotext(); + } catch (err: any) { + missing.push(`pdftotext (${err?.message ?? "unresolvable"})`); + } + // One assertion naming everything missing beats N opaque ones: the fix + // is always "restore the build:gates step / apt packages in free-tests.yml". + expect(missing).toEqual([]); + }); +}); diff --git a/package.json b/package.json index 513393391..dd20d28b6 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dev:make-pdf": "bun run make-pdf/src/cli.ts", "dev:design": "bun run design/src/cli.ts", "build:diagram-render": "cd lib/diagram-render && bun install && bun run scripts/build.ts", + "build:gates": "bun build --compile make-pdf/src/cli.ts --outfile make-pdf/dist/pdf && bun build --compile browse/src/cli.ts --outfile browse/dist/browse && bun run build:diagram-render", "gen:skill-docs": "bun run scripts/gen-skill-docs.ts", "gen:skill-docs:user": "bun run scripts/gen-skill-docs.ts --respect-detection", "dev": "bun run browse/src/cli.ts",