feat(scripts): pre-commit hook + CI gate for compliance drift

Wire pnpm compliance:emit-all into the pre-commit hook (conditional on
staged Payload configs, library traces, or compliance/ files) and add a
hard-fail compliance drift check step to the CI validate job positioned
after pnpm conformance.

Also fix emit-all.mjs: it previously hardcoded --check on every invocation,
so it never actually regenerated artifacts. Now the default mode writes and
--check mode diffs only — matching the pre-commit (write) vs CI (check) split.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 20:14:45 +00:00
parent add54f14ef
commit 239cfcadfa
3 changed files with 41 additions and 13 deletions

View File

@@ -65,6 +65,14 @@ jobs:
- run: pnpm typecheck - run: pnpm typecheck
- run: pnpm lint - run: pnpm lint
- run: pnpm conformance - run: pnpm conformance
- name: Compliance manifest drift check
run: |
pnpm compliance:emit-all --check || {
echo ""
echo "Compliance artifacts are out of date."
echo "Run \`pnpm compliance:emit-all\` locally and commit the updated files."
exit 1
}
- name: Fallow whole-codebase analysis - name: Fallow whole-codebase analysis
run: pnpm fallow --format annotations run: pnpm fallow --format annotations
- run: pnpm turbo boundaries - run: pnpm turbo boundaries

View File

@@ -23,7 +23,14 @@ node scripts/work/state-sync-guard.mjs || exit 1
# 4. Check library decision traces for new runtime deps in feature/core packages. # 4. Check library decision traces for new runtime deps in feature/core packages.
node scripts/library-decisions/check.mjs || exit 1 node scripts/library-decisions/check.mjs || exit 1
# 5. Scan staged changes for secrets (skip gracefully if gitleaks is not installed). # 5. If any staged file touches Payload configs, library traces, or compliance
# artifacts, regenerate compliance YAMLs and auto-stage them.
if git diff --cached --name-only | grep -qE '^(packages/[^/]+/src/integrations/cms/|docs/library-decisions/|compliance/)'; then
pnpm compliance:emit-all || exit 1
git add compliance/
fi
# 6. Scan staged changes for secrets (skip gracefully if gitleaks is not installed).
if command -v gitleaks > /dev/null 2>&1; then if command -v gitleaks > /dev/null 2>&1; then
gitleaks protect --staged --redact || exit 1 gitleaks protect --staged --redact || exit 1
else else

View File

@@ -2,12 +2,15 @@
/** /**
* emit-all.mjs — Compliance artifact orchestrator. * emit-all.mjs — Compliance artifact orchestrator.
* *
* Runs all three compliance emitters in --check mode and exits non-zero * Default: regenerates all three compliance artifacts (write mode).
* if any generator reports a mismatch or validation failure. * --check: diffs each artifact against the committed file; exits non-zero
* if any generator reports a mismatch or validation failure.
* *
* Usage: * Usage:
* node scripts/compliance/emit-all.mjs * node scripts/compliance/emit-all.mjs # regenerate all artifacts
* node scripts/compliance/emit-all.mjs --check # drift check (CI gate)
* pnpm compliance:emit-all * pnpm compliance:emit-all
* pnpm compliance:emit-all --check
*/ */
import { spawnSync } from "node:child_process"; import { spawnSync } from "node:child_process";
@@ -22,11 +25,14 @@ const SCRIPTS = [
"emit-sub-processors.mjs", "emit-sub-processors.mjs",
]; ];
const checkMode = process.argv.includes("--check");
const subArgs = checkMode ? ["--check"] : [];
let anyFailed = false; let anyFailed = false;
for (const script of SCRIPTS) { for (const script of SCRIPTS) {
const scriptPath = path.join(__dirname, script); const scriptPath = path.join(__dirname, script);
const result = spawnSync(process.execPath, [scriptPath, "--check"], { const result = spawnSync(process.execPath, [scriptPath, ...subArgs], {
stdio: "inherit", stdio: "inherit",
}); });
@@ -36,14 +42,21 @@ for (const script of SCRIPTS) {
} }
if (anyFailed) { if (anyFailed) {
process.stderr.write( if (checkMode) {
"\n✗ compliance:emit-all — one or more artifacts are out of date.\n" + process.stderr.write(
" Run each generator to regenerate:\n" + "\n✗ compliance:emit-all — one or more artifacts are out of date.\n" +
" pnpm compliance:data-map\n" + " Run `pnpm compliance:emit-all` to regenerate.\n",
" pnpm compliance:retention-policy\n" + );
" pnpm compliance:sub-processors\n", } else {
); process.stderr.write(
"\n✗ compliance:emit-all — one or more artifacts failed to generate.\n",
);
}
process.exit(1); process.exit(1);
} }
console.log("✓ compliance:emit-all — all artifacts are up to date"); if (checkMode) {
console.log("✓ compliance:emit-all — all artifacts are up to date");
} else {
console.log("✓ compliance:emit-all — all artifacts regenerated");
}