From 7f9d2978386dab504c7129359c9c0aa34b44eb6c Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 10 Oct 2024 20:01:46 +0200 Subject: [PATCH 1/3] workflows/nixpkgs-vet: Make merge check script reusable This is useful for other workflows as well. Originally I thought it couldn't be put in the repo, but it can (just needs another checkout) --- .github/workflows/nixpkgs-vet.yml | 48 +++++---------------------- ci/get-merge-commit.sh | 55 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 39 deletions(-) create mode 100755 ci/get-merge-commit.sh diff --git a/.github/workflows/nixpkgs-vet.yml b/.github/workflows/nixpkgs-vet.yml index 7bfe973a8c364..f08af86822cab 100644 --- a/.github/workflows/nixpkgs-vet.yml +++ b/.github/workflows/nixpkgs-vet.yml @@ -26,52 +26,22 @@ jobs: # This should take 1 minute at most, but let's be generous. The default of 6 hours is definitely too long. timeout-minutes: 10 steps: - # This step has to be in this file, because it's needed to determine which revision of the repository to fetch, and we can only use other files from the repository once it's fetched. + # This checks out the base branch because of pull_request_target + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + with: + path: base + sparse-checkout: ci - name: Resolving the merge commit env: GH_TOKEN: ${{ github.token }} run: | - # This checks for mergeability of a pull request as recommended in - # 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 - - # Retry the API query this many times - retryCount=5 - # Start with 5 seconds, but double every retry - retryInterval=5 - while true; do - echo "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/"$GITHUB_REPOSITORY"/pulls/${{ github.event.pull_request.number }}) - mergeable=$(jq -r .mergeable <<< "$prInfo") - mergedSha=$(jq -r .merge_commit_sha <<< "$prInfo") - - if [[ "$mergeable" == "null" ]]; then - if (( retryCount == 0 )); then - echo "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com/" - exit 1 - else - (( retryCount -= 1 )) || true - - # null indicates that GitHub is still computing whether it's mergeable - # Wait a couple seconds before trying again - echo "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 - echo "The PR can be merged, checking the merge commit $mergedSha" + if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then + echo "Checking the merge commit $mergedSha" echo "mergedSha=$mergedSha" >> "$GITHUB_ENV" else - echo "The PR cannot be merged, it has a merge conflict, skipping the rest.." + echo "Skipping the rest..." fi + rm -rf base - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 if: env.mergedSha with: diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh new file mode 100755 index 0000000000000..8d4877ecc4c36 --- /dev/null +++ b/ci/get-merge-commit.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# This checks for mergeability of a pull request as recommended in +# 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 + +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") + 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 1 + 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" + jq -r .merge_commit_sha <<< "$prInfo" +else + log "The PR has a merge conflict" + exit 1 +fi From 048f4aa537ec81b23454d61f559e31477e4f7af2 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 10 Oct 2024 20:11:56 +0200 Subject: [PATCH 2/3] ci/get-merge-commit.sh: Exit for non-open PRs If PRs aren't open (either merged or closed), GitHub never computes whether the PR is mergeable, so we'd wait forever, which has been happening: https://github.com/NixOS/nixpkgs/actions/runs/11279197077/job/31369348101#step:2:59 --- ci/get-merge-commit.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh index 8d4877ecc4c36..75fc7fcbe60f6 100755 --- a/ci/get-merge-commit.sh +++ b/ci/get-merge-commit.sh @@ -26,6 +26,14 @@ while true; do -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 2 + fi + mergeable=$(jq -r .mergeable <<< "$prInfo") if [[ "$mergeable" == "null" ]]; then if (( retryCount == 0 )); then From e6a8855a14cb86c461df1c1f8585a19c71ac90c1 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 10 Oct 2024 20:40:46 +0200 Subject: [PATCH 3/3] ci/get-merge-commit.sh: Add documentation And distinguish exit codes --- ci/README.md | 55 ++++++++++++++++++++++++++++++++++++++++++ ci/get-merge-commit.sh | 9 +++---- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/ci/README.md b/ci/README.md index 40c3d0ed344b5..11b53c6095e6e 100644 --- a/ci/README.md +++ b/ci/README.md @@ -41,3 +41,58 @@ Why not just build the tooling right from the PRs Nixpkgs version? - 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). + +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 test merge commit hash is 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 can be used in GitHub Actions workflows 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: + build: + name: Build + runs-on: ubuntu-latest + steps: + # Important: Because of `pull_request_target`, this doesn't check out the PR, + # but rather the base branch of the PR, which is needed so we don't run untrusted code + - uses: actions/checkout@ + with: + path: base + sparse-checkout: ci + - name: Resolving the merge commit + env: + GH_TOKEN: ${{ github.token }} + run: | + if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then + echo "Checking the merge commit $mergedSha" + echo "mergedSha=$mergedSha" >> "$GITHUB_ENV" + else + # Skipping so that no notifications are sent + echo "Skipping the rest..." + fi + rm -rf base + - uses: actions/checkout@ + # Add this to _all_ subsequent steps to skip them + if: env.mergedSha + with: + ref: ${{ env.mergedSha }} + - ... +``` diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh index 75fc7fcbe60f6..c62bb56dd993e 100755 --- a/ci/get-merge-commit.sh +++ b/ci/get-merge-commit.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# This checks for mergeability of a pull request as recommended in -# 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 +# See ./README.md for docs set -euo pipefail @@ -31,14 +30,14 @@ while true; do state=$(jq -r .state <<< "$prInfo") if [[ "$state" != open ]]; then log "PR is not open anymore" - exit 2 + 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 1 + exit 3 else (( retryCount -= 1 )) || true @@ -59,5 +58,5 @@ if [[ "$mergeable" == "true" ]]; then jq -r .merge_commit_sha <<< "$prInfo" else log "The PR has a merge conflict" - exit 1 + exit 2 fi