diff --git a/.github/workflows/get-merge-commit.yml b/.github/workflows/get-merge-commit.yml index edbda3e040eb..2bb58c5bedb1 100644 --- a/.github/workflows/get-merge-commit.yml +++ b/.github/workflows/get-merge-commit.yml @@ -22,8 +22,8 @@ jobs: resolve-merge-commit: runs-on: ubuntu-24.04-arm outputs: - mergedSha: ${{ steps.merged.outputs.mergedSha }} - targetSha: ${{ steps.merged.outputs.targetSha }} + mergedSha: ${{ fromJSON(steps.merged.outputs.result).mergedSha }} + targetSha: ${{ fromJSON(steps.merged.outputs.result).targetSha }} systems: ${{ steps.systems.outputs.systems }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -33,24 +33,45 @@ jobs: - name: Check if the PR can be merged and get the test merge commit id: merged - env: - GH_TOKEN: ${{ github.token }} - GH_EVENT: ${{ github.event_name }} - run: | - case "$GH_EVENT" in - push) - echo "mergedSha=${{ github.sha }}" >> "$GITHUB_OUTPUT" - ;; - pull_request*) - if commits=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then - echo -e "Checking the commits:\n$commits" - echo "$commits" >> "$GITHUB_OUTPUT" - else - # Skipping so that no notifications are sent - echo "Skipping the rest..." - fi - ;; - esac + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + if (context.eventName == 'push') return { mergedSha: context.sha } + + for (const retryInterval of [5, 10, 20, 40, 80]) { + console.log("Checking whether the pull request can be merged...") + const prInfo = (await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + })).data + + if (prInfo.state != 'open') throw new Error ("PR is not open anymore.") + + if (prInfo.mergeable == null) { + console.log(`GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`) + await new Promise(resolve => setTimeout(resolve, retryInterval * 1000)) + continue + } + + if (prInfo.mergeable) { + console.log("The PR can be merged.") + + const mergedSha = prInfo.merge_commit_sha + const targetSha = (await github.rest.repos.getCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: prInfo.merge_commit_sha + })).data.parents[0].sha + + console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`) + + return { mergedSha, targetSha } + } else { + throw new Error("The PR has a merge conflict. Skipping the rest...") + } + } + throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.") - name: Load supported systems id: systems diff --git a/ci/README.md b/ci/README.md index 6ef665e8b099..7fcda96e3480 100644 --- a/ci/README.md +++ b/ci/README.md @@ -40,46 +40,3 @@ Why not just build the tooling right from the PRs Nixpkgs version? - Because it makes the CI check very fast, since no Nix builds need to be done, even for mass rebuilds. - Because it improves security, since we don't have to build potentially untrusted code from PRs. The tool only needs a very minimal Nix evaluation at runtime, which can work with [readonly-mode](https://nixos.org/manual/nix/stable/command-ref/opt-common.html#opt-readonly-mode) and [restrict-eval](https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-restrict-eval). - -## `get-merge-commit.sh GITHUB_REPO PR_NUMBER` - -Check whether a PR is mergeable and return the test merge commit as -[computed by GitHub](https://docs.github.com/en/rest/guides/using-the-rest-api-to-interact-with-your-git-database?apiVersion=2022-11-28#checking-mergeability-of-pull-requests) and its parent. - -Arguments: -- `GITHUB_REPO`: The repository of the PR, e.g. `NixOS/nixpkgs` -- `PR_NUMBER`: The PR number, e.g. `1234` - -Exit codes: -- 0: The PR can be merged, the hashes of the test merge commit and the target commit are returned on stdout -- 1: The PR cannot be merged because it's not open anymore -- 2: The PR cannot be merged because it has a merge conflict -- 3: The merge commit isn't being computed, GitHub is likely having internal issues, unknown if the PR is mergeable - -### Usage - -This script is implemented as a reusable GitHub Actions workflow, and can be used as follows: - -```yaml -on: pull_request_target - -# We need a token to query the API, but it doesn't need any special permissions -permissions: {} - -jobs: - get-merge-commit: - # use the relative path of the get-merge-commit workflow yaml here - uses: ./.github/workflows/get-merge-commit.yml - - build: - name: Build - runs-on: ubuntu-24.04 - needs: get-merge-commit - steps: - - uses: actions/checkout@ - # Add this to _all_ subsequent steps to skip them - if: needs.get-merge-commit.outputs.mergedSha - with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} - - ... -``` diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh deleted file mode 100755 index c233f7f91691..000000000000 --- a/ci/get-merge-commit.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -# See ./README.md for docs - -set -euo pipefail - -log() { - echo "$@" >&2 -} - -if (( $# < 2 )); then - log "Usage: $0 GITHUB_REPO PR_NUMBER" - exit 99 -fi -repo=$1 -prNumber=$2 - -# Retry the API query this many times -retryCount=5 -# Start with 5 seconds, but double every retry -retryInterval=5 - -while true; do - log "Checking whether the pull request can be merged" - prInfo=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/$repo/pulls/$prNumber") - - # Non-open PRs won't have their mergeability computed no matter what - state=$(jq -r .state <<< "$prInfo") - if [[ "$state" != open ]]; then - log "PR is not open anymore" - exit 1 - fi - - mergeable=$(jq -r .mergeable <<< "$prInfo") - if [[ "$mergeable" == "null" ]]; then - if (( retryCount == 0 )); then - log "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com/" - exit 3 - else - (( retryCount -= 1 )) || true - - # null indicates that GitHub is still computing whether it's mergeable - # Wait a couple seconds before trying again - log "GitHub is still computing whether this PR can be merged, waiting $retryInterval seconds before trying again ($retryCount retries left)" - sleep "$retryInterval" - - (( retryInterval *= 2 )) || true - fi - else - break - fi -done - -if [[ "$mergeable" == "true" ]]; then - log "The PR can be merged" - mergedSha="$(jq -r .merge_commit_sha <<< "$prInfo")" - echo "mergedSha=$mergedSha" - targetSha="$(gh api "/repos/$repo/commits/$mergedSha" --jq '.parents[0].sha')" - echo "targetSha=$targetSha" -else - log "The PR has a merge conflict" - exit 2 -fi