diff --git a/.github/actions/check-merge-queue/action.yml b/.github/actions/check-merge-queue/action.yml new file mode 100644 index 00000000000..a88578ca9c9 --- /dev/null +++ b/.github/actions/check-merge-queue/action.yml @@ -0,0 +1,91 @@ +name: Check merge queue +description: Check whether a pull request can skip the merge queue when it's + up-to-date with the base branch. + +inputs: + head-ref: + description: The name of the head ref (the pull request branch). + required: true + default: ${{ github.event.merge_group.head_ref }} + + base-ref: + description: The name of the base ref (e.g., main, master). + required: true + default: main + + github-token: + description: The GitHub token to use for authentication. + required: true + default: ${{ github.token }} + +outputs: + up-to-date: + value: ${{ steps.up-to-date.outputs.up-to-date }} + description: Whether the pull request is up-to-date with the base branch + and is first in the merge queue, allowing it to skip the merge queue. + +runs: + using: composite + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Get pull request details + id: pr-details + uses: actions/github-script@v8 + env: + HEAD_REF: ${{ inputs.head-ref }} + with: + github-token: ${{ inputs.github-token }} + script: | + const { HEAD_REF } = process.env; + const match = HEAD_REF.match(/\/pr-([0-9]+)-/u); + if (!match) { + return core.setFailed(`Could not extract pull request number from head ref: "${HEAD_REF}".`); + } + + const number = parseInt(match[1], 10); + const result = await github.graphql(` + query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + headRefName + mergeQueueEntry { position } + } + } + } + `, { + owner: context.repo.owner, + name: context.repo.repo, + number, + }); + + if (!result.repository.pullRequest) { + return core.setFailed(`Pull request #${number} not found in repository "${context.repo.owner}/${context.repo.repo}".`); + } + + const position = result.repository.pullRequest.mergeQueueEntry?.position; + if (!position) { + return core.setFailed(`Pull request #${number} is not in the merge queue.`); + } + + core.setOutput('pr-number', number); + core.setOutput('pr-branch', result.repository.pullRequest.headRefName); + core.setOutput('merge-queue-position', position); + + - name: Check if pull request is up-to-date with base branch + id: up-to-date + shell: bash + env: + BASE_REF: ${{ inputs.base-ref }} + PR_BRANCH: ${{ steps.pr-details.outputs.pr-branch }} + MERGE_QUEUE_POSITION: ${{ steps.pr-details.outputs.merge-queue-position }} + run: | + set -euo pipefail + if git merge-base --is-ancestor "origin/${BASE_REF}" "origin/${PR_BRANCH}" && [ "${MERGE_QUEUE_POSITION}" -eq 1 ]; then + echo "up-to-date=true" >> "$GITHUB_OUTPUT" + else + echo "up-to-date=false" >> "$GITHUB_OUTPUT" + fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ce44020ec80..ecdc86954a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,8 +11,28 @@ concurrency: cancel-in-progress: ${{ !contains(github.ref, 'refs/heads/main') }} jobs: + check-skip-merge-queue: + name: Check if pull request can skip merge queue + if: github.event_name == 'merge_group' + runs-on: ubuntu-latest + outputs: + skip-merge-queue: ${{ steps.check-skip-merge-queue.outputs.up-to-date }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Check pull request merge queue status + id: check-skip-merge-queue + uses: ./.github/actions/check-merge-queue + with: + head-ref: ${{ github.event.merge_group.head_ref }} + base-ref: main + github-token: ${{ secrets.GITHUB_TOKEN }} + check-workflows: name: Check workflows + needs: + - check-skip-merge-queue + if: always() && github.event_name != 'merge_group' || needs.check-skip-merge-queue.outputs.skip-merge-queue != 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -24,6 +44,14 @@ jobs: run: ${{ steps.download-actionlint.outputs.executable }} -color shell: bash + print-job-status: + needs: check-workflows + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - run: echo "${{ job.status }}" + shell: bash + analyse-code: name: Analyse code needs: check-workflows @@ -127,11 +155,14 @@ jobs: name: All jobs pass if: ${{ always() }} runs-on: ubuntu-latest - needs: all-jobs-complete + needs: + - all-jobs-complete + - check-skip-merge-queue + env: + PASSED: ${{ needs.all-jobs-complete.outputs.passed == 'true' || needs.check-skip-merge-queue.outputs.skip-merge-queue == 'true' }} steps: - name: Check that all jobs have passed run: | - passed="${{ needs.all-jobs-complete.outputs.passed }}" - if [[ $passed != "true" ]]; then + if [[ "$PASSED" != "true" ]]; then exit 1 fi