fix(core): identify the test merge from the merge ref, not the payload - #372
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
getTestMergeBaseCommitSha() recognised GitHub's test-merge commit by comparing its second parent against pull_request.head.sha. That value can lag behind the merge ref the runner checked out: GitHub recomputes refs/pull/<n>/merge when either branch moves, without always firing a new event. When a push lands in between, the payload still carries the previous head while the checkout is the newer test-merge commit. The guard then rejected a genuine test-merge build and fell back to the merge base, baselining the pull request against its fork point and reporting every change merged into the base branch since as its own. GITHUB_REF is set by the runner from the ref the run was triggered on, so on a pull_request event it names the merge ref and cannot go stale. Together with the existing check that the build runs on GITHUB_SHA it identifies the test merge without the payload; the head.sha comparison stays as the fallback when the merge ref is absent. The parents.length check is split out so a non-merge HEAD is rejected before either identification path runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gregberge
force-pushed
the
fix/test-merge-ref
branch
from
September 4, 2026 13:25
17dfa8c to
daa02f3
Compare
jsfez
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Follow-up to #371, which reported "older baseline" symptoms in production: pull request builds baselined against their fork point, as if the branch had never been merged with the base branch. No orphan builds — just a stale baseline, so every visual change merged into the base branch since the branch was created came back as a change of the pull request. Exactly the bug #371 set out to fix.
#371 itself is not at fault.
getTestMergeBaseCommitSha()recognises GitHub's test-merge commit by comparing its second parent againstpull_request.head.sharead from the event payload. That value can lag behind the merge ref the runner actually checked out: GitHub recomputesrefs/pull/<n>/mergewhen either branch moves, and does not always fire a new event for it. When a push lands between the event and the recompute, the payload still carries the previous head while the checkout is the newer test-merge commit. The comparison then fails, the guard rejects a genuine test-merge build, and the fallback silently baselines it against the merge base.The staleness is not theoretical — the verification run below shows the payload reporting
base.sha = c0f449d3whilemainwas already at78fe66e7. #371's own description hit the same thing from the other side: PR #360's payloadbase.shawas 26 commits behind the merge ref's real first parent.GITHUB_REFis set by the runner from the ref the run was triggered on, so on apull_requestevent it names the merge ref itself and cannot go stale. Together with the existing check that the build runs onGITHUB_SHA, it identifies the test merge without reading the payload at all. Thehead.shacomparison stays as the fallback for when the merge ref is absent, where it is still the only way to tell GitHub's test merge apart from a merge the author made.The
parents.length !== 2check is also split out of the combined condition, so a non-merge HEAD is rejected before either identification path runs — the merge ref alone must not be trusted when the checkout is not a merge.Type of changes
bugChecklist
Further comments
Verified on a real runner, against a real pull request
Fixtures cannot reproduce this: it depends on shallow clones, hidden commit parents,
refs/pull/<n>/merge, and payload staleness that only exist on a GitHub-hosted runner. The verification harness lives in argos-ci/argos-test-repository#21. It checks out this repository into the workflow, bundlespackages/core/src/ci-environmentandfind-reference-commit.tswith esbuild, and calls the real exported functions — no re-implementation, and noARGOS_TOKENneeded. A matrix overref:runsmainand this branch side by side in a single run, and the expected value is read from the GitHub API by the workflow so the assertion does not depend on the code under test.The test branch is deliberately never rebased:
mainwas advanced twice past its fork point, so the merge base (e5ba9c18) and the commit GitHub merges in (78fe66e7) are different commits.main(12a8c62)78fe66e7✅78fe66e7✅pull_request.head.shae5ba9c18❌ fork point78fe66e7✅On
main, with a stale head:What was ruled out along the way
The same harness confirmed the rest of the path is sound, so the fix could stay narrow:
actions/checkout@v6and@v4defaults,fetch-depth: 0,fetch-depth: 50,persist-credentials: falseall resolve78fe66e7. The only configuration that falls back isref: refs/pull/<n>/head, which is correct: those screenshots do not contain the base branch changes, so the merge base is the right baseline for them.--depth=2deepening fetch ingetCommitParents()is load-bearing and works. On a defaultfetch-depth: 1checkoutgit rev-list --parentsreturns the commit alone; the parents only appear after the fetch.resolveBaseline()offers the server a complete candidate list. With/baselinestubbed to find nothing, the CLI sendsreferenceCommit = 78fe66e7plus every ancestor down past the fork point.Reviewer notes
/baselinedoes return a build,resolveBaseline()sendsparentCommits: null(find-reference-commit.ts:176). Server-side,listParentCommitShasthen falls through to the git provider, which returns[]for a light app installation. So for light-app projects the server gets a single commit and no ancestor chain — ifgetBaseBucketForBuildAndCommitmisses on it, the build has no fallback at all. That is pre-existing and independent of this fix, but fix(core): baseline PR builds against the commit GitHub merged in #371 made its input riskier: the reference commit is now the base branch tip, the commit most likely to still be building or unapproved, where before it was an old settled fork point.head.shain the event file. The mechanism is confirmed by the realbase.shastaleness observed in the same run.🤖 Generated with Claude Code