From 5f7d76fd5ad652592ad6d572f6e51945f0fdce1e Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 8 Oct 2026 13:27:38 +0200 Subject: [PATCH] ci: run the new size, boundary, layering, offline and i18n gates in CI Each gate came with its npm script but ran nowhere. The server job now runs lint:size, lint:boundaries and shared's contracts:open after the shared build; the client job runs lint:warnings in place of lint:check (it fails on errors the same way and also holds the warning count per rule), then lint:format, lint:layers, lint:offline, lint:skips and lint:i18n-keys. All of them are steps of jobs ci-ok already needs. Both size baselines are taken on the merged branch. Four server files grew while the other changes landed (the claimed reminder, the email uniqueness error and the generator rule for an expression index) and four client files by two or three lines with the plural forms; one client file shrank and is lowered. --- .github/workflows/test.yml | 50 ++++++++++++++++++++++++++++--- CLAUDE.md | 11 +++++-- client/scripts/size-baseline.json | 10 +++---- server/scripts/size-baseline.json | 8 ++--- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 618b98ef8..ab1195c95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -246,6 +246,23 @@ jobs: - name: DB call-graph gate (no sync DB-touching methods, no raw transactions) run: cd server && npm run db:call-graph -- --sync --tx + - name: File size check + # A source file may hold no more lines than scripts/size-baseline.json + # allows (1000 for a file without an entry, long lines counted + # wrapped), so a big service is split by concern instead of growing. + run: cd server && npm run lint:size + + - name: Import boundaries and cycles + # No new load-time cycle, no new reach into another domain's + # internals, src/db stays below src/nest. Today's violations sit in + # scripts/import-boundaries-baseline.json and may only shrink. + run: cd server && npm run lint:boundaries + + - name: Open request schemas may only shrink + # Needs the shared build above. Counts open shapes (records of + # unknown, passthrough, any) in shared's request schemas. + run: cd shared && npm run contracts:open + client-quality: name: Client Types & Lint needs: changes @@ -272,7 +289,9 @@ jobs: run: cd client && npm run typecheck - name: Lint - run: cd client && npm run lint:check + # Fails on any ESLint error, like lint:check did, and on any rule + # whose warning count rises above scripts/eslint-baseline.json. + run: cd client && npm run lint:warnings - name: Page pattern check run: cd client && npm run lint:pages @@ -284,11 +303,34 @@ jobs: run: cd client && npm run lint:rtl - name: File size check - # A source file may hold no more lines than scripts/size-baseline.json - # allows (1000 for a file without an entry), so a big file is split by - # concern instead of growing (scripts/size-lint.mjs). + # A source file or stylesheet may hold no more lines than + # scripts/size-baseline.json allows (1000 without an entry, 2000 for a + # test, long lines counted wrapped), so a big file is split by concern + # instead of growing (scripts/size-lint.mjs). run: cd client && npm run lint:size + - name: Formatting check + # Every file outside scripts/format-baseline.json must be Prettier + # formatted; the list only shrinks. + run: cd client && npm run lint:format + + - name: Layering check + # Imports go downwards: components never import pages/ or mobile/. + run: cd client && npm run lint:layers + + - name: Offline data path check + # No view file calls src/api/ directly beyond + # scripts/offline-baseline.json; writes go through repo/ and the + # mutation queue. + run: cd client && npm run lint:offline + + - name: Skipped and focused tests check + run: cd client && npm run lint:skips + + - name: i18n keys check + # Every translation key src/ names must exist in shared en. + run: cd client && npm run lint:i18n-keys + plugin-sdk-quality: name: Plugin SDK Types needs: changes diff --git a/CLAUDE.md b/CLAUDE.md index 32e3cf399..a15100de3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,14 @@ cd client && npm run test # client vitest run cd client && npm run e2e # Playwright (CI runs the public and app projects; the screenshot and help-media ones are local) cd client && npm run lint:pages # enforce the Page pattern (CI gate) cd client && npm run lint:rtl # physical left/right sides may only shrink per file (CI gate) -cd client && npm run lint:size # no source file past 1000 lines, longer ones only shrink (CI gate) +cd client && npm run lint:size # no source file past 1000 lines, longer ones only shrink (CI gate; server has the same) +cd client && npm run lint:warnings # eslint errors fail, warnings per rule may only shrink (CI gate) +cd client && npm run lint:format # Prettier outside the baseline list (CI gate) +cd client && npm run lint:layers # components never import pages/ or mobile/ (CI gate) +cd client && npm run lint:offline # views may not import src/api/ beyond the baseline (CI gate) +cd client && npm run lint:skips # no .only; skipped tests may only shrink (CI gate) +cd client && npm run lint:i18n-keys # every key the client names exists in en (CI gate) +cd server && npm run lint:boundaries # no new import cycles or cross-domain reach-ins (CI gate) cd client && npm run theme:lint # flag styling that bypasses appearance tokens (local only) npm run typecheck --workspace=server # tsc --noEmit (also client/shared; server also has typecheck:tests) ``` @@ -110,7 +117,7 @@ These principles come out of a full-repo audit and shape all new code: ## Quality gates in CI -`.github/workflows/test.yml` runs, per package: typecheck (server also `typecheck:tests`), `lint:check`, `lint:pages`, `lint:rtl`, `lint:size`, `check:plugin-facts`, `i18n:parity:strict`, the S3 contract suite, the browser end-to-end flows (Chromium and WebKit), a client build with `check:gl-split`, coverage for all four packages, then a SonarCloud scan. It also builds the Docker image and boots it until `/api/health/ready` answers (`docker-image`), and lints and renders the Helm chart against the Kubernetes schemas and runs `docker compose config` on every compose file (`deploy-manifests`). `lint-prettier.yml` additionally runs `lint` + `format:check` on `shared/`. `plugin-sdk` has no lint script and is outside the eslint gates. **Not in CI**: `theme:lint` and the screenshot and help-media Playwright projects; run them locally. There are no git hooks; nothing runs before a commit except you. Other workflows build Docker images, scan them, and publish `trek-plugin-sdk` on `plugin-sdk-v*` tags. +`.github/workflows/test.yml` runs, per package: typecheck (server also `typecheck:tests`), lint (client through `lint:warnings`, which also holds the warning count per rule), `lint:pages`, `lint:rtl`, `lint:size` (client and server), the server's `lint:boundaries`, the client's `lint:format`, `lint:layers`, `lint:offline`, `lint:skips` and `lint:i18n-keys`, shared's `contracts:open`, `check:plugin-facts`, `i18n:parity:strict` (with the untranslated-strings ratchet), the S3 contract suite, the browser end-to-end flows (Chromium and WebKit), a client build with `check:gl-split`, coverage for all four packages, then a SonarCloud scan. It also builds the Docker image and boots it until `/api/health/ready` answers (`docker-image`), and lints and renders the Helm chart against the Kubernetes schemas and runs `docker compose config` on every compose file (`deploy-manifests`). `lint-prettier.yml` additionally runs `lint` + `format:check` on `shared/`. `plugin-sdk` has no lint script and is outside the eslint gates. **Not in CI**: `theme:lint` and the screenshot and help-media Playwright projects; run them locally. There are no git hooks; nothing runs before a commit except you. Other workflows build Docker images, scan them, and publish `trek-plugin-sdk` on `plugin-sdk-v*` tags. - **`ci-ok` is the required check.** The branch ruleset requires `ci-ok` (plus `lint` and `check-target` from their own workflows), and `ci-ok` needs every job above except the scan. It fails if any of them failed or was cancelled and passes if they passed or were skipped. A new gate job counts only once it is listed in `ci-ok`'s `needs:`. - **What runs is decided by `changes`, not by `on.paths`**: `scripts/ci/changed-areas.mjs` maps the changed files to the `code`, `image` and `deploy` areas (tests: `npm run test:ci-scripts`). A path filter on the workflow would leave `ci-ok` unreported on a docs-only PR and block it. Add a new top-level file the build reads to the right area there. diff --git a/client/scripts/size-baseline.json b/client/scripts/size-baseline.json index 88d00cfe6..e62581dfe 100644 --- a/client/scripts/size-baseline.json +++ b/client/scripts/size-baseline.json @@ -2,7 +2,7 @@ "e2e/help/fixtures.ts": 2291, "src/api/client.ts": 1916, "src/components/Admin/AdminPluginsPanel.test.tsx": 2704, - "src/components/Admin/AdminPluginsPanel.tsx": 2431, + "src/components/Admin/AdminPluginsPanel.tsx": 2433, "src/components/Budget/CostsPanel.test.tsx": 2854, "src/components/Budget/CostsPanel.tsx": 2366, "src/components/Journey/JourneyDetailPageEntryEditor.tsx": 1014, @@ -12,8 +12,8 @@ "src/components/Map/RouteCalculator.ts": 1196, "src/components/PDF/TripPDF.tsx": 1097, "src/components/Planner/DayDetailPanel.test.tsx": 2333, - "src/components/Planner/DayPlanSidebar.test.tsx": 5400, - "src/components/Planner/DayPlanSidebar.tsx": 3319, + "src/components/Planner/DayPlanSidebar.test.tsx": 5403, + "src/components/Planner/DayPlanSidebar.tsx": 3312, "src/components/Planner/PlaceFormModal.test.tsx": 2864, "src/components/Planner/PlaceFormModal.tsx": 1669, "src/components/Planner/PlaceInspector.tsx": 1357, @@ -23,7 +23,7 @@ "src/components/Studio/bookTemplates.data.ts": 1309, "src/components/Studio/countryShapes.ts": 2595, "src/index.css": 1636, - "src/mobile/screens/admin/MAdminPluginsPanel.tsx": 2262, + "src/mobile/screens/admin/MAdminPluginsPanel.tsx": 2264, "src/mobile/screens/journey/MJourneyEntrySheet.tsx": 1122, "src/mobile/screens/trip/sheets/MTransportFormSheet.tsx": 1194, "src/mobile/screens/trip/tabs/MCostsTab.tsx": 1095, @@ -34,6 +34,6 @@ "src/pages/TripPlannerPage.wiring.test.tsx": 2049, "src/pages/atlas/useAtlas.ts": 1288, "src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx": 3531, - "src/pages/tripPlanner/useTripPlanner.test.tsx": 3307, + "src/pages/tripPlanner/useTripPlanner.test.tsx": 3309, "src/styles/studio.css": 2087 } diff --git a/server/scripts/size-baseline.json b/server/scripts/size-baseline.json index d652438fe..14cf0dcd4 100644 --- a/server/scripts/size-baseline.json +++ b/server/scripts/size-baseline.json @@ -1,15 +1,15 @@ { "scripts/db-call-graph.mjs": 1036, - "scripts/generate-entities.ts": 2186, + "scripts/generate-entities.ts": 2259, "src/db/dialect/sql-functions.ts": 1007, "src/db/repositories/DayAssignments.repository.ts": 1030, "src/db/repositories/Places.repository.ts": 1483, "src/db/repositories/Reservations.repository.ts": 1806, - "src/db/repositories/Trips.repository.ts": 1145, - "src/db/repositories/Users.repository.ts": 1679, + "src/db/repositories/Trips.repository.ts": 1164, + "src/db/repositories/Users.repository.ts": 1710, "src/nest/atlas/atlas-geo.ts": 1005, "src/nest/atlas/atlas.service.ts": 1182, - "src/nest/auth/auth.service.ts": 1161, + "src/nest/auth/auth.service.ts": 1166, "src/nest/budget/budget.service.ts": 1703, "src/nest/collections/collections.service.ts": 1488, "src/nest/doc-sync/doc-sync.service.ts": 1060,