mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
fix(governance): align pre-push guard with CI
This commit is contained in:
+22
-8
@@ -43,14 +43,28 @@ fi
|
||||
CHANGED=$(git diff --name-only "$BASE"...HEAD)
|
||||
CHANGED_GO=$(printf '%s\n' "$CHANGED" | grep -E '\.go$' || true)
|
||||
|
||||
step "Canonical completion guard (HEAD commit, CI mode)"
|
||||
if [ "$AHEAD" -gt 1 ]; then
|
||||
echo "Note: $AHEAD commits ahead; the guard is checked for HEAD only here, CI checks each commit."
|
||||
fi
|
||||
if ! git diff --name-only HEAD~1 HEAD | \
|
||||
python3 scripts/release_control/canonical_completion_guard.py --files-from-stdin --diff-base HEAD~1; then
|
||||
fail "canonical completion guard"
|
||||
fi
|
||||
step "Canonical completion guard (per commit, CI mode)"
|
||||
while IFS= read -r commit; do
|
||||
if [ -z "$commit" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! git rev-parse --verify --quiet "${commit}^" >/dev/null; then
|
||||
echo "Skipping root commit $commit (no parent to diff against)."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Match canonical-governance.yml exactly: the prepare-commit-msg hook
|
||||
# persists an intentional local bypass as a trailer, so recover that
|
||||
# reason when validating already-created commits before push.
|
||||
reason=$(git log -1 --format='%(trailers:key=Contract-Neutral,valueonly,separator=; )' "$commit" | tr '\n' ' ')
|
||||
echo "Checking $commit"
|
||||
if ! git diff-tree --no-commit-id --name-only -r "$commit" | \
|
||||
PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT="$reason" \
|
||||
python3 scripts/release_control/canonical_completion_guard.py \
|
||||
--files-from-stdin --diff-base "${commit}^"; then
|
||||
fail "canonical completion guard @ $commit"
|
||||
fi
|
||||
done < <(git rev-list --reverse --no-merges "$BASE"..HEAD)
|
||||
|
||||
if printf '%s\n' "$CHANGED" | grep -q 'docs/release-control/v6/internal/subsystems/registry.json'; then
|
||||
step "Registry snapshot tests (registry.json changed)"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import redirect_stderr
|
||||
from pathlib import Path
|
||||
@@ -3746,5 +3747,73 @@ class ReleaseCycleArtifactGuardTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class DevPrepushScriptTest(unittest.TestCase):
|
||||
def test_checks_every_commit_with_its_contract_neutral_trailer(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
repo = Path(temp_dir)
|
||||
(repo / "scripts" / "release_control").mkdir(parents=True)
|
||||
(repo / "scripts" / "dev-prepush.sh").write_text(
|
||||
(REPO_ROOT / "scripts" / "dev-prepush.sh").read_text(encoding="utf-8"),
|
||||
encoding="utf-8",
|
||||
)
|
||||
(repo / "scripts" / "release_control" / "canonical_completion_guard.py").write_text(
|
||||
"""#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
files = ",".join(line.strip() for line in sys.stdin if line.strip())
|
||||
with open(os.environ["PULSE_TEST_GUARD_LOG"], "a", encoding="utf-8") as log:
|
||||
reason = os.environ.get("PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT", "").strip()
|
||||
log.write(f"{reason}\\t{files}\\n")
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
def git(*args: str) -> None:
|
||||
subprocess.run(
|
||||
["git", *args],
|
||||
cwd=repo,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
git("init", "-q", "-b", "main")
|
||||
git("config", "user.name", "Pulse Test")
|
||||
git("config", "user.email", "pulse-test@example.invalid")
|
||||
git("add", "scripts")
|
||||
git("commit", "-q", "-m", "base")
|
||||
git("tag", "base")
|
||||
|
||||
(repo / "first.txt").write_text("first\n", encoding="utf-8")
|
||||
git("add", "first.txt")
|
||||
git(
|
||||
"commit",
|
||||
"-q",
|
||||
"-m",
|
||||
"first change\n\nContract-Neutral: dependency-only test",
|
||||
)
|
||||
(repo / "second.txt").write_text("second\n", encoding="utf-8")
|
||||
git("add", "second.txt")
|
||||
git("commit", "-q", "-m", "second change")
|
||||
|
||||
guard_log = repo / "guard.log"
|
||||
env = os.environ.copy()
|
||||
env["PULSE_TEST_GUARD_LOG"] = str(guard_log)
|
||||
result = subprocess.run(
|
||||
["bash", "scripts/dev-prepush.sh", "base"],
|
||||
cwd=repo,
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
self.assertEqual(
|
||||
guard_log.read_text(encoding="utf-8").splitlines(),
|
||||
["dependency-only test\tfirst.txt", "\tsecond.txt"],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user