From 4a53177bc5ec2f357b3046fab15e3579f1ab4e04 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Sat, 10 Oct 2026 07:36:18 +1300 Subject: [PATCH] ci(build): build pull requests merged into current main GitHub refreshes merge_commit_sha only when the pull request's branch changes, so a manual build could test the pull request against a main from days earlier. Each job now checks out main as it is when the build starts and merges the pull request head into it. --- .github/workflows/build.yml | 46 ++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c067651f3..8ac64417d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,6 +36,8 @@ jobs: pull-requests: read outputs: sha: ${{ steps.gate.outputs.sha }} + pr: ${{ steps.gate.outputs.pr }} + head: ${{ steps.gate.outputs.head }} relinker: ${{ steps.gate.outputs.relinker }} full: ${{ steps.gate.outputs.full }} steps: @@ -50,6 +52,8 @@ jobs: throw new Error('Automatic full builds only run on main'); } core.setOutput('sha', context.sha); + core.setOutput('pr', ''); + core.setOutput('head', ''); core.setOutput('relinker', true); core.setOutput('full', true); await core.summary.addRaw(`Building main at ${context.sha}: all builds and tests enabled`).write(); @@ -83,7 +87,7 @@ jobs: if (!manual && pr.head.sha !== context.payload.pull_request.head.sha) { throw new Error('The pull request has newer commits; use the latest run'); } - if (pr.mergeable !== true || !pr.merge_commit_sha) { + if (pr.mergeable === false) { throw new Error('The pull request does not merge cleanly into main; rebase it first'); } let relinker = manual; @@ -93,11 +97,15 @@ jobs: }); relinker = commits.length > 0; } - core.setOutput('sha', pr.merge_commit_sha); + const { data: main } = await github.rest.repos.getBranch({ owner, repo, branch: 'main' }); + core.setOutput('sha', main.commit.sha); + core.setOutput('pr', String(pull_number)); + core.setOutput('head', pr.head.sha); core.setOutput('relinker', relinker); core.setOutput('full', manual); + core.info(`PR #${pull_number} at ${pr.head.sha} merged into main at ${main.commit.sha}`); await core.summary.addRaw( - `PR #${pull_number} at ${pr.head.sha} merged into main as ${pr.merge_commit_sha}: full builds ${manual ? 'enabled' : 'disabled'}, ` + + `PR #${pull_number} at ${pr.head.sha} merged into main at ${main.commit.sha}: full builds ${manual ? 'enabled' : 'disabled'}, ` + `relinker ${relinker ? 'enabled' : 'skipped because the author has no commit in main'}` ).write(); @@ -127,6 +135,16 @@ jobs: with: ref: ${{ needs.eligibility.outputs.sha }} persist-credentials: false + - name: Merge the pull request into main + if: ${{ needs.eligibility.outputs.head != '' }} + shell: bash + env: + PR_NUMBER: ${{ needs.eligibility.outputs.pr }} + PR_HEAD: ${{ needs.eligibility.outputs.head }} + run: | + git fetch --no-tags --unshallow origin "refs/pull/$PR_NUMBER/head" + if [ "$(git rev-parse FETCH_HEAD)" != "$PR_HEAD" ]; then echo "::error::The pull request has newer commits; start a new build"; exit 1; fi + if ! git -c user.name=ci -c user.email=ci@localhost merge --no-ff --no-edit FETCH_HEAD; then echo "::error::The pull request does not merge cleanly into main; rebase it first"; exit 1; fi - if: runner.os == 'Windows' id: toolchain uses: actions/cache@v6 @@ -154,6 +172,17 @@ jobs: ref: ${{ needs.eligibility.outputs.sha }} persist-credentials: false submodules: true + - name: Merge the pull request into main + if: ${{ needs.eligibility.outputs.head != '' }} + shell: bash + env: + PR_NUMBER: ${{ needs.eligibility.outputs.pr }} + PR_HEAD: ${{ needs.eligibility.outputs.head }} + run: | + git fetch --no-tags --unshallow origin "refs/pull/$PR_NUMBER/head" + if [ "$(git rev-parse FETCH_HEAD)" != "$PR_HEAD" ]; then echo "::error::The pull request has newer commits; start a new build"; exit 1; fi + if ! git -c user.name=ci -c user.email=ci@localhost merge --no-ff --no-edit FETCH_HEAD; then echo "::error::The pull request does not merge cleanly into main; rebase it first"; exit 1; fi + git submodule update --init --recursive - uses: hendrikmuhs/ccache-action@v1.2.24 with: key: linux @@ -196,6 +225,17 @@ jobs: ref: ${{ needs.eligibility.outputs.sha }} persist-credentials: false submodules: true + - name: Merge the pull request into main + if: ${{ needs.eligibility.outputs.head != '' }} + shell: bash + env: + PR_NUMBER: ${{ needs.eligibility.outputs.pr }} + PR_HEAD: ${{ needs.eligibility.outputs.head }} + run: | + git fetch --no-tags --unshallow origin "refs/pull/$PR_NUMBER/head" + if [ "$(git rev-parse FETCH_HEAD)" != "$PR_HEAD" ]; then echo "::error::The pull request has newer commits; start a new build"; exit 1; fi + if ! git -c user.name=ci -c user.email=ci@localhost merge --no-ff --no-edit FETCH_HEAD; then echo "::error::The pull request does not merge cleanly into main; rebase it first"; exit 1; fi + git submodule update --init --recursive - id: toolchain uses: actions/cache@v6 with: