feat(ci): catalogue-drift gate for @RequirePrivilege/@RequireRole literals (ADR-0025)
CI / commits (pull_request) Successful in 2m18s
CI / check (pull_request) Successful in 2m57s
CI / scan (pull_request) Successful in 3m5s
CI / a11y (pull_request) Successful in 3m11s
Docs site / build (pull_request) Successful in 3m24s
CI / perf (pull_request) Successful in 6m57s
CI / commits (pull_request) Successful in 2m18s
CI / check (pull_request) Successful in 2m57s
CI / scan (pull_request) Successful in 3m5s
CI / a11y (pull_request) Successful in 3m11s
Docs site / build (pull_request) Successful in 3m24s
CI / perf (pull_request) Successful in 6m57s
Phase 3 of the ADR-0025 phasing per its §"More Information" §347:
a small CI gate that asserts every string literal passed to the
authorization decorators belongs to the closed catalogue declared
in libs/shared/auth/src/lib/authorization.types.ts.
The TypeScript signature on the decorators already enforces this
at compile time — the new gate is defence-in-depth against escape
hatches (`as Privilege` casts, hand-edits to the type union
without updating the runtime constant). It runs in <1s, so it
rides the existing `check` CI job rather than starting its own.
Implementation:
- scripts/check-catalogue-drift.mjs: TypeScript compiler API.
Parses authorization.types.ts to extract the catalogue arrays,
walks every .ts file under apps/ and libs/, finds each
CallExpression whose callee identifier matches @RequirePrivilege
or @RequireRole, validates every string-literal argument.
Non-literal args (variable indirection) are skipped on purpose —
the TypeScript signature catches them at compile time and the
gate's value-add is on literal misspellings. Generated gRPC
stubs under apps/portal-bff/src/grpc/gen are skipped (their
`roles[]` field is a wire-format unrelated to the catalogue).
Reports grouped by file with line:column for each violation,
exits 1 on drift.
- scripts/check-catalogue-drift.spec.mjs: 13 tests via node:test
(built-in, no Vitest dependency). Covers catalogue parsing,
file-level violation detection, workspace-wide aggregation,
skipped folders, gen-stub exclusion, line/column reporting.
- package.json: ci:catalogue-drift + ci:catalogue-drift:test
scripts.
- .gitea/workflows/ci.yml: the `check` job now runs the gate's
unit tests (first, to fail fast if the gate itself is broken)
then the gate against the live workspace.
Self-tested by injecting `RequirePrivilege('Portal.RogueDrift')`
and `RequireRole('rogue-role-x')` into an existing spec — both
caught with file:line:column and exit code 1.
Test plan:
- pnpm ci:catalogue-drift:test: 13/13 green.
- pnpm ci:catalogue-drift: clean (4 privileges, 24 roles).
- pnpm nx affected -t format:check lint test build: no project affected (script lives outside Nx).
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
"prepare": "husky",
|
||||
"ci:check": "pnpm exec nx affected -t format:check lint test build",
|
||||
"ci:audit": "pnpm audit --audit-level=moderate",
|
||||
"ci:catalogue-drift": "node scripts/check-catalogue-drift.mjs",
|
||||
"ci:catalogue-drift:test": "node --test scripts/check-catalogue-drift.spec.mjs",
|
||||
"ci:commits": "pnpm exec commitlint --from ${COMMIT_LINT_FROM:-origin/main} --to HEAD --verbose",
|
||||
"ci:gzip-budgets": "node scripts/check-gzip-budgets.mjs",
|
||||
"ci:perf": "pnpm exec nx build portal-shell --configuration=production && pnpm ci:gzip-budgets && pnpm exec lhci autorun --config=./lighthouserc.js",
|
||||
|
||||
Reference in New Issue
Block a user