From f3db9527d141014586c1f664d2a812a1489949c7 Mon Sep 17 00:00:00 2001 From: Vibe Code Date: Sat, 8 Aug 2026 00:54:34 +0200 Subject: [PATCH] docs-ci: build the site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every check in this workflow reads content and compares it against something. None of them ran Next. That is how a docs site that could not be built sat on main behind a green tick — twice: a floating base-image tag that took the Docker build with it, and a duplicated @base-ui/react that left a React context unresolvable at prerender. Neither was visible to a single gate in this file. A separate job, so it runs alongside the content checks and a failure says which half broke. It installs with --frozen-lockfile, because the committed lockfile is what deploys and a build that only passes after resolution drifts is not a pass. It runs Next through Node directly, the same command web-docs/Dockerfile runs, with the same NEXT_PUBLIC_URL — gating the path that ships rather than a convenient approximation. Pinned in fchub-ci-contract so the gate cannot quietly disappear later. Mutation-checked: replace the build step with an echo and the contract fails. --- .github/workflows/docs-ci.yml | 39 ++++++++++++++++++++ .github/workflows/fchub-ci-contract.test.mjs | 9 +++++ 2 files changed, 48 insertions(+) diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index 9db8b260..d1a0b049 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -77,6 +77,45 @@ concurrency: cancel-in-progress: true jobs: + # The site has to actually build. Every check below this one reads content + # and compares it against something; none of them run Next, which is how a + # docs site that cannot be built sat on main behind a green tick. Two real + # breakages hid here: a floating base-image tag that took the Docker build + # with it, and a duplicated @base-ui/react that left a React context + # unresolvable at prerender. Both were invisible to every gate in this file. + build: + name: Site builds + runs-on: ubuntu-latest + defaults: + run: + working-directory: web-docs + steps: + - uses: actions/checkout@v7 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.14 + + - uses: actions/setup-node@v7 + with: + node-version: '26' + + # --frozen-lockfile on purpose: the committed lockfile is what deploys, + # so a build that only passes after resolution drifts is not a pass. + - name: Install + run: bun install --frozen-lockfile + + # Node, not bun, and Next invoked directly — the same command + # web-docs/Dockerfile runs, so this gate exercises the path that ships + # rather than a convenient local approximation. NEXT_PUBLIC_URL matches + # the Dockerfile's default for the same reason. + - name: Build + env: + NODE_ENV: production + NEXT_TELEMETRY_DISABLED: '1' + NEXT_PUBLIC_URL: https://fchub.co + run: node ./node_modules/next/dist/bin/next build + consistency: name: Version Consistency runs-on: ubuntu-latest diff --git a/.github/workflows/fchub-ci-contract.test.mjs b/.github/workflows/fchub-ci-contract.test.mjs index 1f908f77..b0b92bc8 100644 --- a/.github/workflows/fchub-ci-contract.test.mjs +++ b/.github/workflows/fchub-ci-contract.test.mjs @@ -207,6 +207,15 @@ test('Docs CI watches the FCHub sources its own checks read', () => { const consistency = job(docs, 'consistency') assert.match(consistency, /node scripts\/check-fchub-docs\.mjs/) assert.match(consistency, /node scripts\/sync-fchub-catalog\.mjs --check/) + + // Every other check in Docs CI reads content and compares it to something. + // None of them runs Next, which is how a site that could not be built sat + // on main behind a green tick. Pinned so the gate cannot quietly go away: + // it must run the same command the Dockerfile does, on the committed + // lockfile, or it is testing something other than what deploys. + const build = job(docs, 'build') + assert.match(build, /bun install --frozen-lockfile/) + assert.match(build, /node \.\/node_modules\/next\/dist\/bin\/next build/) }) /**