From 239cfcadfa9d223540db9f797906c1006075aca3 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 18 May 2026 20:14:45 +0000 Subject: [PATCH] feat(scripts): pre-commit hook + CI gate for compliance drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/ci.yml | 8 +++++++ .husky/pre-commit | 9 +++++++- scripts/compliance/emit-all.mjs | 37 ++++++++++++++++++++++----------- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31512a6..0397496 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,14 @@ jobs: - run: pnpm typecheck - run: pnpm lint - 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 run: pnpm fallow --format annotations - run: pnpm turbo boundaries diff --git a/.husky/pre-commit b/.husky/pre-commit index 725f9bd..6c2832e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -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. 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 gitleaks protect --staged --redact || exit 1 else diff --git a/scripts/compliance/emit-all.mjs b/scripts/compliance/emit-all.mjs index 46711e8..ee3ddbc 100644 --- a/scripts/compliance/emit-all.mjs +++ b/scripts/compliance/emit-all.mjs @@ -2,12 +2,15 @@ /** * emit-all.mjs — Compliance artifact orchestrator. * - * Runs all three compliance emitters in --check mode and exits non-zero - * if any generator reports a mismatch or validation failure. + * Default: regenerates all three compliance artifacts (write mode). + * --check: diffs each artifact against the committed file; exits non-zero + * if any generator reports a mismatch or validation failure. * * 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 --check */ import { spawnSync } from "node:child_process"; @@ -22,11 +25,14 @@ const SCRIPTS = [ "emit-sub-processors.mjs", ]; +const checkMode = process.argv.includes("--check"); +const subArgs = checkMode ? ["--check"] : []; + let anyFailed = false; for (const script of SCRIPTS) { const scriptPath = path.join(__dirname, script); - const result = spawnSync(process.execPath, [scriptPath, "--check"], { + const result = spawnSync(process.execPath, [scriptPath, ...subArgs], { stdio: "inherit", }); @@ -36,14 +42,21 @@ for (const script of SCRIPTS) { } if (anyFailed) { - process.stderr.write( - "\n✗ compliance:emit-all — one or more artifacts are out of date.\n" + - " Run each generator to regenerate:\n" + - " pnpm compliance:data-map\n" + - " pnpm compliance:retention-policy\n" + - " pnpm compliance:sub-processors\n", - ); + if (checkMode) { + process.stderr.write( + "\n✗ compliance:emit-all — one or more artifacts are out of date.\n" + + " Run `pnpm compliance:emit-all` to regenerate.\n", + ); + } else { + process.stderr.write( + "\n✗ compliance:emit-all — one or more artifacts failed to generate.\n", + ); + } 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"); +}