Files
Claude 8e55964b14 Refresh the catalogues on a schedule, and republish when they change
Mondays 05:23 UTC: re-run the ETL cold against the live archives, and if the
output differs by a byte, gate it on the unit suite and a production build,
commit it to main, and dispatch the Pages deploy. If nothing changed, say so
in the run summary and touch nothing.

The gates run inside this workflow because they cannot run after it: a push
made with GITHUB_TOKEN fires no push workflows at all — GitHub's recursion
guard — so an unguarded push would deploy nothing and be checked by nothing.
The same guard is why the deploy and a visible CI record are dispatched
explicitly afterwards; dispatch events do go through where push events do
not. ci.yml gains a workflow_dispatch trigger for exactly that call.

Also corrects pages.yml's claim that configure-pages enables Pages on first
run. It cannot: the action's `enablement` input requires an admin-scoped
token, which GITHUB_TOKEN is not. If the site has never been enabled, the
first deploy fails at that step and the one-time fix is Settings → Pages →
Source: GitHub Actions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
2026-08-17 14:05:42 +00:00

87 lines
2.9 KiB
YAML

name: CI
# Runs on pull requests and on the branches they merge into, so a green tick means the code was
# checked in the state it will actually land in. `develop` is where work integrates and `main` is
# what it is promoted to; a merge into either is a state nothing else would otherwise check,
# since a pull request is checked before the merge rather than after it.
on:
push:
branches: [main, develop]
pull_request:
# For the data-refresh workflow: its bot push to main fires no `push` events (GitHub's
# recursion guard), so it dispatches CI here explicitly to put checks on the new commit.
workflow_dispatch:
# A second push to the same branch makes the first run's answer irrelevant.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
checks:
name: Typecheck, unit tests, build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
# `npm ci` rather than `npm install`: it installs exactly what package-lock.json pins and
# fails if the lockfile has drifted from package.json, so CI cannot silently test a
# different dependency tree than the one committed.
- run: npm ci
# Four TypeScript projects, checked by four different things. These two have no build of
# their own, so nothing else would ever compile them.
- name: Typecheck the ETL
run: npm run etl:typecheck
- name: Typecheck the end-to-end tests
run: npm run e2e:typecheck
# `tsconfig.spec.json` is compiled here, `tsconfig.app.json` by the build below.
- name: Unit tests
run: npm test -- --no-watch
- name: Production build
run: npm run build
e2e:
name: End-to-end
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
- run: npm ci
# `--with-deps` installs the system libraries headless Chromium needs, which a bare runner
# does not have. Only chromium: playwright.config.ts defines no other project.
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
# Playwright starts the dev server itself (see `webServer` in playwright.config.ts).
# GitHub sets CI=true, which turns on `forbidOnly` and the two retries.
- name: End-to-end tests
run: npm run e2e
# The HTML reporter's output is the only way to see why a headless browser failed. Only
# kept when something did fail — on a green run it is several megabytes saying so.
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7