Commit the lockfile, and give the repository CI

Nothing has ever checked this project automatically. There are no workflows,
so the pull request's green tick means only that the checks were run by hand
on one machine, and nothing would catch a regression pushed later.

The lockfile had to come first. `npm ci` is the only install that guarantees
CI tests the dependency tree that is actually committed, and it refuses to
run without package-lock.json — which was gitignored. Un-ignoring it also
pins the 617 packages this was built and verified against; without it, a
transitive release could change what CI runs from one day to the next with
no commit to point at. Checked before committing: every entry resolves to
registry.npmjs.org, and it carries no credentials.

Two jobs rather than one, run in parallel. The typecheck/unit/build job is
fast and deterministic; the end-to-end job drives a real headless browser
through WebGL2 software rendering and is the one that will be slow and, if
anything here is going to be flaky, flaky. Keeping them apart means a
browser timeout cannot hide a failing unit test behind it.

Between the four steps, all four TypeScript projects are compiled: the ETL
and end-to-end configs explicitly, since nothing else ever builds them, and
the spec and app configs by `ng test` and `ng build` respectively.

One thing this cannot verify from here: the runner installs Chromium to
match the pinned Playwright, where this container ships an older build. The
suite was run locally against that older browser instead, and passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
This commit is contained in:
Claude
2026-08-05 09:57:21 +00:00
parent 8191254b3c
commit 2e67c3ea9a
3 changed files with 9663 additions and 1 deletions
+81
View File
@@ -0,0 +1,81 @@
name: CI
# Runs on pull requests and on the branch they merge into, so a green tick means the code was
# checked in the state it will actually land in.
on:
push:
branches: [main]
pull_request:
# 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