build: the gate is scripts/check.sh, and CI goes
The Gitea Actions gate repeated, after the merge, what a release already runs before it, and its runner shared the git host's container: one run there starved Gitea of memory until it stopped answering. The lanes now live in scripts/check.sh, run the same by the release dispatcher and by hand, and the gate-contract test holds its lines (the ruff and pip-audit checks read it instead of the workflow). The two audits only CI ran join the gate: pip-audit over the lock (pinned in the test image, blocking at any severity) and npm audit of the production dependencies. The engine contracts drop the CI containers; the register records that pushes have no server-side gate.
This commit is contained in:
+20
-4
@@ -5,7 +5,7 @@ How to run Siftlode locally and pass the gate. Conventions for the backend code
|
||||
|
||||
## Node
|
||||
|
||||
One major, everywhere. `.nvmrc` pins it; the CI container, the `Dockerfile`'s frontend build stage,
|
||||
One major, everywhere. `.nvmrc` pins it; the `Dockerfile`'s frontend build stage,
|
||||
`frontend/package.json`'s `engines` and `@types/node` must all match it — and so must the machine you
|
||||
run the gate on. `frontend/src/lib/nodeEngine.contract.test.ts` fails the gate the moment any of them
|
||||
drift apart, including when yours is the odd one out. `nvm use` in the repo root picks it up.
|
||||
@@ -38,9 +38,20 @@ decisions and the chosen patterns, rendered with the app's real CSS. It never re
|
||||
|
||||
## The gate
|
||||
|
||||
A change is done when all of this is green. CI runs the same checks server-side
|
||||
(`.gitea/workflows/check.yml`), so "main is always green". What each gate deliberately does NOT
|
||||
check — and why — is the [gate register](gates.md); a test fails when a narrowing is missing from it.
|
||||
A change is done when this is green, from the repo root:
|
||||
|
||||
```bash
|
||||
bash scripts/check.sh # every lane
|
||||
bash scripts/check.sh lint # the static lanes only (no tests, build or audits) — the quick loop
|
||||
```
|
||||
|
||||
It runs every lane even after one fails and names each red one at the end. What it runs, and why
|
||||
each lane runs where it does, is in the script's comments; `frontend/src/lib/gates.contract.test.ts`
|
||||
holds its lines, so a lane dropped or narrowed there fails a test. What each gate deliberately does
|
||||
NOT check — and why — is the [gate register](gates.md). There is no CI: the gate runs here, and a
|
||||
release runs it (plus e2e and the CVE scan) before anything is merged.
|
||||
|
||||
The lanes, for running one by hand:
|
||||
|
||||
**Backend**
|
||||
|
||||
@@ -58,6 +69,10 @@ docker compose -f backend/docker-compose.test.yml run --rm --no-deps --build tes
|
||||
# coverage total. The backend tree is bind-mounted, so it always tests your working files.
|
||||
docker compose -f backend/docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from test
|
||||
docker compose -f backend/docker-compose.test.yml down -v
|
||||
|
||||
# the lock against the advisory databases, at any severity
|
||||
docker compose -f backend/docker-compose.test.yml run --rm --no-deps test \
|
||||
pip-audit -r requirements.lock --no-deps --disable-pip
|
||||
```
|
||||
|
||||
A missing database or ffmpeg is a hard error, not a skip — so the gate can't print green while the
|
||||
@@ -75,6 +90,7 @@ npm run format:check # prettier
|
||||
npm run knip # unused files/exports/deps — and unused by what SHIPS (--production)
|
||||
npm run test # vitest (incl. the compiler, size/complexity and gate-register contracts)
|
||||
npx vite build # the production bundle (devOnlyGuard fails it on dev-only code)
|
||||
npm audit --omit=dev --audit-level=high # what ships to the browser, at high or above
|
||||
```
|
||||
|
||||
Coverage is reported, never enforced: `npm run test -- --coverage`, and the pytest lane's last line.
|
||||
|
||||
+5
-5
@@ -14,10 +14,10 @@ name its invariant. Whatever is left narrowed is listed here, and
|
||||
not an oversight; a narrowing missing from here is a test failure.
|
||||
|
||||
**The snapshot.** ESLint's effective config for every code file `eslint .` reaches, and the lines
|
||||
that run the lanes (`.gitea/workflows/check.yml`, `backend/docker-compose.test.yml`,
|
||||
that run the lanes (`scripts/check.sh`, `backend/docker-compose.test.yml`,
|
||||
`backend/Dockerfile.test`, the npm scripts, `vitest.config.ts`, `.prettierignore`), are committed in
|
||||
[`frontend/src/lib/gates.snapshot.json`](../frontend/src/lib/gates.snapshot.json). The test fails on
|
||||
ANY difference — a rule, an option, a glob that moves one file to another config, a new ignore, a CI
|
||||
ANY difference — a rule, an option, a glob that moves one file to another config, a new ignore, a gate
|
||||
flag. So a change to a gate is always a diff of that file in review, never a shape a detector failed
|
||||
to model (E32 review: three rounds of a cleverer detector each found a bypass of the last one's
|
||||
fixes). Regenerate it with `GATE_SNAPSHOT=update npx vitest run src/lib/gates.contract.test.ts`
|
||||
@@ -117,17 +117,17 @@ project checks needs a row.
|
||||
| Scope | Narrowed | Why | Measured |
|
||||
| --- | --- | --- | --- |
|
||||
| check | e2e (Playwright) not in `check` | It needs the :8080 stack built from the tree (minutes). It runs in `check-full`, and `release` rebuilds the stack and requires it (E32 D2) — nothing ships without it. | 5 specs × 3 engines |
|
||||
| CI | no e2e, no Trivy | The lab runner has no browser stack, and Trivy's DB download is not reachable from it. Both run in `release` / `publish` on the dev machine, before anything is pushed. | — |
|
||||
| pushes | no server-side gate (no CI) | Owner decision 2026-10-04. The gate (`scripts/check.sh`) runs on the dev machine before work is handed over, and `release` runs it, then e2e, before anything is merged and Trivy before the image is pushed — the CI only repeated that, after the merge. Its runner shared the git host's container, and one run there starved Gitea of memory until it stopped answering. What it added — a fresh `npm ci` on Linux — the image build does at publish. | — |
|
||||
| coverage | no threshold | Owner decision E32 D4: reported on both sides, never enforced. "Every regression test seen red" is the bar; a percentage invites tests written to move it. | frontend 80% / backend 78% of lines (E32 S1) |
|
||||
| CVE gate | HIGH/CRITICAL with a fix available only | `--ignore-unfixed` + `--severity HIGH,CRITICAL`: a finding with no fix cannot be acted on by an upgrade; it shows in the scan output. A fixable one fails the publish, and the only way past it is `.trivyignore.yaml`. | 0 open (E32 S1) |
|
||||
| npm audit (CI) | `--omit=dev --audit-level=high` | What ships to the browser, at high or above. The dev toolchain's advisories were burned down by hand in E32 S1 (18 → 0). | 0 |
|
||||
| npm audit | `--omit=dev --audit-level=high` | What ships to the browser, at high or above. The dev toolchain's advisories were burned down by hand in E32 S1 (18 → 0). | 0 |
|
||||
| React Compiler | the functions it skips, pinned | `components/compilerCoverage.contract.test.ts` — the list may only shrink. End state: only VirtualGrid (TanStack Virtual, an IncompatibleLibrary) remains. | 93 (E32 S1) → 29 (E32 S3) → 1, VirtualGrid (E32 S4) |
|
||||
| size / complexity | today's offenders as ceilings | `src/lib/sourceSize.contract.test.ts`, `backend/tests/test_source_size.py`: listed files and functions may not grow, nothing new may cross the limit, an entry back under it must be deleted. A function is held by its name, not its rank in the file. | files >800 lines: 13 FE / 9 BE; functions over the limit: 64 FE / 32 BE (E32 review) |
|
||||
| size / complexity | shipped code only — not tests, test kits, e2e or `scripts/` | The ratchet guards what runs in production. A test file is long because it is a list of cases, and a test kit is test code; splitting either moves lines without making anything easier to change. `scripts/` is release and seed tooling, not in the image. | outside it: tests >800 lines 2 FE / 5 BE; functions over the limit 2 FE test code (`gates.contract.test.ts` `eslintNarrowings` 23, E32 review round 3; `compilerSkips.testkit.ts` 25), 1 BE test (11), 1 script (`e2e_seed.py` `main` 13) |
|
||||
|
||||
## Accepted findings
|
||||
|
||||
Entries in `.trivyignore.yaml` (CVE gate) and `--ignore-vuln` flags on CI's `pip-audit` step. Each
|
||||
Entries in `.trivyignore.yaml` (CVE gate) and `--ignore-vuln` flags on the gate's `pip-audit` lane (`scripts/check.sh`). Each
|
||||
must have a row here (Scope `accepted`) with its reason and its expiry; the test fails on an entry
|
||||
without one, and on a `.trivyignore.yaml` entry with no `statement` or no `expired_at`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user