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:
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user