ci(gate): add PR diff guard to block stale destructive diffs #2905
Reference in New Issue
Block a user
Delete Branch "fix/2875-pr-diff-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #2875.
Adds
.gitea/workflows/pr-diff-guard.yml+.gitea/scripts/pr-diff-guard.py. The guard compares the PR head against the merge base of the target branch and fails when:.gitea/workflows/,.gitea/scripts/,tests/e2e/,workspace-server/internal/handlers/,workspace-server/internal/provisioner/,workspace-server/internal/middleware/,canvas/src/)Motivated by #2875 / PR #1100, where a stale branch carried a 481-file, ~55k-line destructive diff while
CI/all-requiredstayed green.Test plan
python3 -m py_compile .gitea/scripts/pr-diff-guard.py— passes.python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/pr-diff-guard.yml'))"— passes.PR_BASE_REF=main PR_HEAD_SHA=$(git rev-parse HEAD) python3 .gitea/scripts/pr-diff-guard.py→ passed for a 2-file, +165-line diff.🤖 Generated with Claude Code
SOP Checklist
APPROVE (1st genuine). head
a3e5a91. Enforcement-strengthening — confirmed it cannot weaken/bypass any existing gate.This is a purely additive gate (new
pr-diff-guard.py+ workflow) that BLOCKS the #1100-class catastrophic drift (481 files / ~55k deletions on a stale branch). 5-axis + enforcement scrutiny:merge-base(origin/base, head)..head, so it measures what the PR adds (not main drift); rebasing a stale branch clears it (intended). ANY deletion under a protected path (.gitea/workflows,.gitea/scripts, handlers, provisioner, middleware,canvas/src) fails regardless of count — strong targeted-deletion catch.Non-blocking notes: (1) the workflow is not yet wired into branch protection (documented: "bp-required: pending #2875" — advisory until thresholds calibrate + green history). So it RUNS but doesn't BLOCK merge until separately required — intended, honest. (2)
git diff --name-statusflagsDdeletions but notRrenames, so a rename that relocates a protected file isn't caught as a protected-deletion — minor edge. required-5 green. Approving.