Commit Graph
7 Commits
Author SHA1 Message Date
peter c9101e3450 ci: type-check the backend — the gate's ninth lane (E27 S3)
Three tsc projects held the frontend and NOTHING held the backend: ~36k
lines of `app/`, annotated in 113 of 128 files, that no lane had ever
read. Asymmetric by accident, not by decision.

basedpyright, in basic mode. Measured against mypy on this tree with the
dependencies actually installed — without them basedpyright errors on
every import and mypy's --ignore-missing-imports quietly turns them into
Any and UNDER-reports. Both land ~190-200 errors with the same
distribution, so the choice fell to what differs: basedpyright is ~2x
faster (4.4s vs 7.4s, and this runs on every `siftlode lint`), resolves
our `app.*` imports from its own config with no MYPYPATH, and has a
first-class `--writebaseline`. That last one is the ratchet itself;
mypy would have needed a third-party tool for it.

198 pre-existing errors are baselined, so the lane fails on NEW errors
only and the list burns down epic by epic. A green lane means no new
damage, not a clean codebase.

It runs in the pytest test image rather than on the host, and that is
forced rather than chosen: `requirements.lock` cannot be installed on
Windows at all — uvloop refuses to build there — so a host venv could
only ever be the lock minus a package, and a type checker without the
real third-party packages reports nonsense rather than nothing. The
image installs the pin from requirements-dev.txt, so the lane's version
IS the pin and there is no host copy to drift. The cost is that
`siftlode lint` now needs Docker, which it did not before.
2026-09-20 23:02:02 +02:00
peter 09d351a0e7 docs(ci): the two gate jobs queue, they do not run in parallel
Declared side by side, so they read as parallel; the lab runs a single
act_runner with one slot, so the backend job starts at the exact second the
frontend one ends. Measured across runs #86-#91: ~4 + ~6 = ~10 minutes from the
push, not ~6.

Written down because believing the comment cost something real — the ops
release read-back budgeted the max instead of the sum and timed out on its
first live run, reporting a still-running gate as a failing one.
2026-09-20 22:05:56 +02:00
peter 1453fa4a33 ci: run the gate on branch pushes only, not on release tags
A release pushes main and then the v<ver> tag, and a bare `on: [push]` treated
the tag as a second push of the identical commit — every release ran the whole
gate twice to the same verdict (runs #81-#90 are five such pairs).

It has to be `branches`, not the `tags-ignore` this obviously wants to be:
Gitea's matchPushEvent requires every named filter to match and each one bails
on the wrong ref kind, so `tags-ignore` rejects every BRANCH push and silently
disables the gate. Measured in go-gitea v1.27.3, where it differs from GitHub.

A trigger filter that stops firing is invisible, so the ops `release` now reads
the verdict back for the sha it pushed and says so when there is no run for it.
2026-09-20 19:47:19 +02:00
peter efe106dfda build: pin one Node major across the dev, CI and build machines
The server gate ran node:22 while the dev gate ran node:24, and the two disagree about
Intl.NumberFormat's compact currency padding and about which realm's Blob a Response hands
back — so the frontend job had been red for 73 of its last 90 runs while the local gate was
green, and nothing said so.

Align everything on 24, with `.nvmrc` as the single source of truth: the CI container, the
prod build stage, `engines`, and `@types/node` (which was two majors ahead of the runtime, so
tsc was green over APIs the shipped engine does not have). The backend job's node only runs
the checkout action, so it tracks the pin as hygiene — node 20 is EOL — but deliberately
stays outside the contract.

The pin is held by a contract test that reads every one of those places AND the engine
running it, so the next drift is a red lane on its first run instead of a CI log nobody reads.

Verified on node:24-bookworm (the CI lane: 228 files / 1552 tests, typecheck, lint,
format:check, knip all exit 0) and on node:24-alpine (the prod build stage, v24.21.0, SPA
built). Each assertion of the new test was mutation-checked red.
2026-09-20 18:06:24 +02:00
peter 434a98d6ed chore(lint): set ruff line-length 100 and gate ruff format --check
Adds ruff's formatter to the gate. line-length 100 matches the frontend
prettier printWidth. The reformat itself lands as the next commit (kept
separate so it can go in .git-blame-ignore-revs).
2026-08-23 07:42:42 +02:00
peter 44d4e4876d build(deps): pin a reproducible lock + pip-audit; bump node 20->22 (E1 S4, SEC-5/DX-3)
requirements.txt stays the source (floor ranges); requirements.lock pins the
exact resolved versions (generated in the python:3.12-slim prod base, extras like
psycopg-binary/uvloop included). The prod Dockerfile, Dockerfile.test and CI now
install the lock, so the image is reproducible and the versions are the ones
pip-audit scans. CI gains a non-blocking pip-audit step over the lock. The
frontend build + CI move from the EOL node 20 to node 22.
2026-08-18 02:34:45 +02:00
peter 54ba92bb05 ci(gitea): add the server-side gate mirror (E1 S3, DX-1)
gate / backend (push) Successful in 1m13s
gate / frontend (push) Successful in 51s
Add .gitea/workflows/check.yml so the quality gate runs on the lab act_runner,
machine-independent, on every push/PR — the review's "no server-side CI" gap.
Two parallel jobs mirror the two local lanes:
- frontend (node:20-bookworm, matching the prod build image): tsc + eslint +
  prettier --check + knip + vitest.
- backend (nikolaik/python-nodejs python3.12+node): ruff + the full pytest suite
  against a real postgres:16 service, with ffmpeg installed and
  SIFTLODE_TEST_REQUIRE_DB=1 so a DB/ffmpeg that failed to come up is a hard
  error, never a silent skip.

Notes for this runner: pytest runs natively against the service (the
docker-compose bind-mount is unreliable under a mounted-socket runner), and the
backend uses a baked python+node image rather than actions/setup-python, which
can't reach GitHub's python-versions download from the lab.
2026-08-18 01:59:36 +02:00