Skip to content

fix(actions): handle merge commits in verify-signed-commit-authors - #40

Merged
darkgnotic merged 1 commit into
mainfrom
darkgnotic/fix-signature-verification
Sep 23, 2026
Merged

darkgnotic merged 1 commit into
mainfrom
darkgnotic/fix-signature-verification

Conversation

@darkgnotic

Copy link
Copy Markdown
Contributor

Summary

verify-signed-commit-authors fails PRs whose branch contains a merge commit (e.g. Merge branch 'main' into feature-branch), even when every commit is validly signed.

Root cause: the action shallow-fetches the PR head with --depth=(commitCount + 1), a hop-count budget that assumes linear history — distance to the merge-base equals the PR's commit count. A merge commit breaks that assumption: the true divergence point from the base branch can sit much further back down the merged-in side than commitCount + 1 hops. When the shallow fetch's depth budget runs out first, git grafts a parentless shallow boundary that severs the graph link proving that commit is also an ancestor of the base branch. git rev-list base..head then can't prove shared ancestry across that boundary, so it overcounts (Expected N, found N+1) — with no relation to whether any commit is actually signed correctly.

Repro'd against rocicorp/mono PR #6651 and traced the exact shallow-graft mechanism; details in the linked internal conversation. --shallow-exclude=<baseSha> was evaluated as an exact-boundary alternative to the depth heuristic, but GitHub's smart-HTTP server doesn't support deepen-not — confirmed via packet trace (fatal: expected 'packfile'). A fully unbounded fetch of the head ref also works but roughly doubles fetch cost on every run (measured ~22s/620MB → ~44s/1.1GB on mono), since it doesn't dedupe against the already-fetched base.

Fix

Keep the cheap depth-limited fetch as the fast path (no behavior change for ordinary linear-history PRs). On a count mismatch, retry with git fetch --deepen=N (N starting at commitCount × 4, growing ×4 per attempt, capped at 4 attempts) before falling back to a full --unshallow fetch as a last resort. Only fails for real if the count still doesn't converge after that.

Validated live against rocicorp/mono PR #6651 (previously failing with "Expected 4, found 5"): now passes, converging via a single deepen call, with fetch time/size in the same ballpark as today's baseline (not the ~2x cost of the always-unbounded alternative).

Test plan

  • node --test .github/actions/verify-signed-commit-authors/verify-signed-commit-authors.test.mjs — 20/20 pass, including 3 new tests covering deepen-convergence, unshallow-fallback, and fail-closed
  • Ran the actual script against rocicorp/mono PR #6651's real base/head SHAs with a live GitHub token — now verifies all 4 commits correctly

🤖 Generated with Claude Code

A PR branch with a merge commit (e.g. merging the base branch in) can
have its true merge-base much further back on the merged-in side than
the initial shallow fetch's depth budget (prCommitCount + 1) reaches,
since that budget assumes linear history. When the budget is exhausted
mid-chain, git grafts a parentless shallow boundary that severs the
link proving shared ancestry with the base branch, so rev-list
overcounts and the check fails even though every commit is validly
signed.

Retry with a deepened fetch (doubling^2 each attempt, capped) before
falling back to a full unshallow fetch as a last resort, so the common
linear-history case stays cheap and only merge-commit branches pay for
the extra round trips.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@arv arv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@darkgnotic
darkgnotic added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 06a0ebd Sep 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants