diff --git a/.github/workflows/gate-attestation.yml b/.github/workflows/gate-attestation.yml index 206d256..baff49b 100644 --- a/.github/workflows/gate-attestation.yml +++ b/.github/workflows/gate-attestation.yml @@ -38,22 +38,19 @@ jobs: - name: Verify Gate-Passed trailer if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.user.login != 'release-please[bot]' && !startsWith(github.head_ref, 'release-please--branches--') }} + # WHY (#2399): bind to the PR TIP, not "any PR commit". The old loop + # passed if any ancestor carried a Gate-Passed trailer, so an un-stamped + # tip could ride an earlier commit's attestation — a false green on the + # actually-merged tree. + env: + # WHY: head.sha reaches the script via env, not shell interpolation + # (expression-injection rule). It is server-populated + content-addressed. + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | - commits=$(git log --format="%H" "origin/${{ github.base_ref }}..HEAD") - if [ -z "$commits" ]; then - echo "ERROR: No commits found in PR" - exit 1 - fi - found=false - for sha in $commits; do - body=$(git log -1 --format="%b" "$sha") - if echo "$body" | grep -q "^Gate-Passed:"; then - echo "Found gate attestation: $(echo "$body" | grep '^Gate-Passed:' | head -1)" - found=true - break - fi - done - if [ "$found" = false ]; then - echo "ERROR: No Gate-Passed trailer found in any PR commit." + body=$(git log -1 --format="%b" "$PR_HEAD_SHA") + if echo "$body" | grep -q "^Gate-Passed:"; then + echo "Found gate attestation on PR tip: $(echo "$body" | grep '^Gate-Passed:' | head -1)" + else + echo "ERROR: PR tip carries no Gate-Passed trailer (ancestors' trailers do not count — #2399)." exit 1 fi diff --git a/.github/workflows/hybrid-gate.yml b/.github/workflows/hybrid-gate.yml index 214e0f7..3174559 100644 --- a/.github/workflows/hybrid-gate.yml +++ b/.github/workflows/hybrid-gate.yml @@ -210,22 +210,21 @@ jobs: # are the changesets MOST in need of a compile). A trailer NOT # found is a normal outcome routed to full-gate-build — this step # never exits 1. + # WHY (#2399): bind to the PR TIP, not "any PR commit" — an un-stamped + # tip must not ride an ancestor's trailer to skip full-gate-build. + env: + # WHY: head.sha reaches the script via env, not shell interpolation. + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | - commits=$(git log --format="%H" "origin/${{ github.base_ref }}..HEAD") - found=false - for sha in $commits; do - body=$(git log -1 --format="%b" "$sha") - if echo "$body" | grep -q "^Gate-Passed:"; then - echo "Found gate attestation: $(echo "$body" | grep '^Gate-Passed:' | head -1)" - found=true - break - fi - done - - echo "found=$found" >> "$GITHUB_OUTPUT" - if [ "$found" = false ]; then - echo "No Gate-Passed trailer found; routing to full-gate-build." + body=$(git log -1 --format="%b" "$PR_HEAD_SHA") + if echo "$body" | grep -q "^Gate-Passed:"; then + echo "Found gate attestation on PR tip: $(echo "$body" | grep '^Gate-Passed:' | head -1)" + found=true + else + found=false + echo "PR tip carries no Gate-Passed trailer (ancestors do not count — #2399); routing to full-gate-build." fi + echo "found=$found" >> "$GITHUB_OUTPUT" full-gate-build: name: full-gate-build