ci/request-reviews: request reviewers 1-by-1 (#370857)

authored by Silvan Mosberger and committed by GitHub c5eba572 7488963c

+178 -174
+1 -1
.github/workflows/codeowners-v2.yml
··· 106 106 run: nix-build ci -A requestReviews 107 107 108 108 - name: Request reviews 109 - run: result/bin/request-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE" 109 + run: result/bin/request-code-owner-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE" 110 110 env: 111 111 GH_TOKEN: ${{ steps.app-token.outputs.token }}
+9 -9
.github/workflows/eval.yml
··· 254 254 - name: Build the requestReviews derivation 255 255 run: nix-build base/ci -A requestReviews 256 256 257 - - name: Tagging pull request 257 + - name: Labelling pull request 258 258 run: | 259 259 # Get all currently set rebuild labels 260 260 gh api \ ··· 283 283 -f "labels[]=$toAdd" 284 284 done < <(comm -13 before after) 285 285 286 + env: 287 + GH_TOKEN: ${{ github.token }} 288 + REPOSITORY: ${{ github.repository }} 289 + NUMBER: ${{ github.event.number }} 290 + 291 + - name: Requesting maintainer reviews 292 + run: | 286 293 # maintainers.json contains GitHub IDs. Look up handles to request reviews from. 287 294 # There appears to be no API to request reviews based on GitHub IDs 288 295 jq -r 'keys[]' comparison/maintainers.json \ 289 296 | while read -r id; do gh api /user/"$id" --jq .login; done \ 290 - | GH_TOKEN=${{ steps.app-token.outputs.token }} result/bin/process-reviewers.sh "$REPOSITORY" "$NUMBER" "$AUTHOR" \ 291 - > reviewers.json 292 - 293 - # Request reviewers from maintainers of changed output paths 294 - GH_TOKEN=${{ steps.app-token.outputs.token }} gh api \ 295 - --method POST \ 296 - /repos/"$REPOSITORY"/pulls/"$NUMBER"/requested_reviewers \ 297 - --input reviewers.json 297 + | GH_TOKEN=${{ steps.app-token.outputs.token }} result/bin/request-reviewers.sh "$REPOSITORY" "$NUMBER" "$AUTHOR" 298 298 299 299 env: 300 300 GH_TOKEN: ${{ github.token }}
+3 -3
ci/request-reviews/default.nix
··· 14 14 src = lib.fileset.toSource { 15 15 root = ./.; 16 16 fileset = lib.fileset.unions [ 17 - ./get-reviewers.sh 18 - ./process-reviewers.sh 19 - ./request-reviews.sh 17 + ./get-code-owners.sh 18 + ./request-reviewers.sh 19 + ./request-code-owner-reviews.sh 20 20 ./verify-base-branch.sh 21 21 ./dev-branches.txt 22 22 ];
ci/request-reviews/get-reviewers.sh ci/request-reviews/get-code-owners.sh
-65
ci/request-reviews/process-reviewers.sh
··· 1 - #!/usr/bin/env bash 2 - 3 - # Process reviewers for a PR, reading line-separated usernames on stdin, 4 - # returning a JSON suitable to be consumed by the API endpoint to request reviews: 5 - # https://docs.github.com/en/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request 6 - 7 - set -euo pipefail 8 - 9 - log() { 10 - echo "$@" >&2 11 - } 12 - 13 - if (( "$#" < 3 )); then 14 - log "Usage: $0 BASE_REPO PR_NUMBER PR_AUTHOR" 15 - exit 1 16 - fi 17 - 18 - baseRepo=$1 19 - prNumber=$2 20 - prAuthor=$3 21 - 22 - tmp=$(mktemp -d) 23 - trap 'rm -rf "$tmp"' exit 24 - 25 - declare -A users=() 26 - while read -r handle && [[ -n "$handle" ]]; do 27 - users[${handle,,}]= 28 - done 29 - 30 - # Cannot request a review from the author 31 - if [[ -v users[${prAuthor,,}] ]]; then 32 - log "One or more files are owned by the PR author, ignoring" 33 - unset 'users[${prAuthor,,}]' 34 - fi 35 - 36 - gh api \ 37 - -H "Accept: application/vnd.github+json" \ 38 - -H "X-GitHub-Api-Version: 2022-11-28" \ 39 - "/repos/$baseRepo/pulls/$prNumber/reviews" \ 40 - --jq '.[].user.login' > "$tmp/already-reviewed-by" 41 - 42 - # And we don't want to rerequest reviews from people who already reviewed 43 - while read -r user; do 44 - if [[ -v users[${user,,}] ]]; then 45 - log "User $user is a code owner but has already left a review, ignoring" 46 - unset 'users[${user,,}]' 47 - fi 48 - done < "$tmp/already-reviewed-by" 49 - 50 - for user in "${!users[@]}"; do 51 - if ! gh api \ 52 - -H "Accept: application/vnd.github+json" \ 53 - -H "X-GitHub-Api-Version: 2022-11-28" \ 54 - "/repos/$baseRepo/collaborators/$user" >&2; then 55 - log "User $user is not a repository collaborator, probably missed the automated invite to the maintainers team (see <https://github.com/NixOS/nixpkgs/issues/234293>), ignoring" 56 - unset 'users[$user]' 57 - fi 58 - done 59 - 60 - # Turn it into a JSON for the GitHub API call to request PR reviewers 61 - jq -n \ 62 - --arg users "${!users[*]}" \ 63 - '{ 64 - reviewers: $users | split(" "), 65 - }'
+82
ci/request-reviews/request-code-owner-reviews.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + # Requests reviews for a PR after verifying that the base branch is correct 4 + 5 + set -euo pipefail 6 + tmp=$(mktemp -d) 7 + trap 'rm -rf "$tmp"' exit 8 + SCRIPT_DIR=$(dirname "$0") 9 + 10 + log() { 11 + echo "$@" >&2 12 + } 13 + 14 + effect() { 15 + if [[ -n "${DRY_MODE:-}" ]]; then 16 + log "Skipping in dry mode:" "${@@Q}" 17 + else 18 + "$@" 19 + fi 20 + } 21 + 22 + if (( $# < 3 )); then 23 + log "Usage: $0 GITHUB_REPO PR_NUMBER OWNERS_FILE" 24 + exit 1 25 + fi 26 + baseRepo=$1 27 + prNumber=$2 28 + ownersFile=$3 29 + 30 + log "Fetching PR info" 31 + prInfo=$(gh api \ 32 + -H "Accept: application/vnd.github+json" \ 33 + -H "X-GitHub-Api-Version: 2022-11-28" \ 34 + "/repos/$baseRepo/pulls/$prNumber") 35 + 36 + baseBranch=$(jq -r .base.ref <<< "$prInfo") 37 + log "Base branch: $baseBranch" 38 + prRepo=$(jq -r .head.repo.full_name <<< "$prInfo") 39 + log "PR repo: $prRepo" 40 + prBranch=$(jq -r .head.ref <<< "$prInfo") 41 + log "PR branch: $prBranch" 42 + prAuthor=$(jq -r .user.login <<< "$prInfo") 43 + log "PR author: $prAuthor" 44 + 45 + extraArgs=() 46 + if pwdRepo=$(git rev-parse --show-toplevel 2>/dev/null); then 47 + # Speedup for local runs 48 + extraArgs+=(--reference-if-able "$pwdRepo") 49 + fi 50 + 51 + log "Fetching Nixpkgs commit history" 52 + # We only need the commit history, not the contents, so we can do a tree-less clone using tree:0 53 + # https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-quick-summary 54 + git clone --bare --filter=tree:0 --no-tags --origin upstream "${extraArgs[@]}" https://github.com/"$baseRepo".git "$tmp"/nixpkgs.git 55 + 56 + log "Fetching the PR commit history" 57 + # Fetch the PR 58 + git -C "$tmp/nixpkgs.git" remote add fork https://github.com/"$prRepo".git 59 + # This remote config is the same as --filter=tree:0 when cloning 60 + git -C "$tmp/nixpkgs.git" config remote.fork.partialclonefilter tree:0 61 + git -C "$tmp/nixpkgs.git" config remote.fork.promisor true 62 + 63 + git -C "$tmp/nixpkgs.git" fetch --no-tags fork "$prBranch" 64 + headRef=$(git -C "$tmp/nixpkgs.git" rev-parse refs/remotes/fork/"$prBranch") 65 + 66 + log "Checking correctness of the base branch" 67 + if ! "$SCRIPT_DIR"/verify-base-branch.sh "$tmp/nixpkgs.git" "$headRef" "$baseRepo" "$baseBranch" "$prRepo" "$prBranch" | tee "$tmp/invalid-base-error" >&2; then 68 + log "Posting error as comment" 69 + if ! response=$(effect gh api \ 70 + --method POST \ 71 + -H "Accept: application/vnd.github+json" \ 72 + -H "X-GitHub-Api-Version: 2022-11-28" \ 73 + "/repos/$baseRepo/issues/$prNumber/comments" \ 74 + -F "body=@$tmp/invalid-base-error"); then 75 + log "Failed to post the comment: $response" 76 + fi 77 + exit 1 78 + fi 79 + 80 + log "Requesting reviews from code owners" 81 + "$SCRIPT_DIR"/get-code-owners.sh "$tmp/nixpkgs.git" "$ownersFile" "$baseBranch" "$headRef" | \ 82 + "$SCRIPT_DIR"/request-reviewers.sh "$baseRepo" "$prNumber" "$prAuthor"
+83
ci/request-reviews/request-reviewers.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + # Request reviewers for a PR, reading line-separated usernames on stdin, 4 + # filtering for valid reviewers before using the API endpoint to request reviews: 5 + # https://docs.github.com/en/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request 6 + 7 + set -euo pipefail 8 + 9 + tmp=$(mktemp -d) 10 + trap 'rm -rf "$tmp"' exit 11 + 12 + log() { 13 + echo "$@" >&2 14 + } 15 + 16 + effect() { 17 + if [[ -n "${DRY_MODE:-}" ]]; then 18 + log "Skipping in dry mode:" "${@@Q}" 19 + else 20 + "$@" 21 + fi 22 + } 23 + 24 + if (( "$#" < 3 )); then 25 + log "Usage: $0 BASE_REPO PR_NUMBER PR_AUTHOR" 26 + exit 1 27 + fi 28 + 29 + baseRepo=$1 30 + prNumber=$2 31 + prAuthor=$3 32 + 33 + tmp=$(mktemp -d) 34 + trap 'rm -rf "$tmp"' exit 35 + 36 + declare -A users=() 37 + while read -r handle && [[ -n "$handle" ]]; do 38 + users[${handle,,}]= 39 + done 40 + 41 + # Cannot request a review from the author 42 + if [[ -v users[${prAuthor,,}] ]]; then 43 + log "One or more files are owned by the PR author, ignoring" 44 + unset 'users[${prAuthor,,}]' 45 + fi 46 + 47 + gh api \ 48 + -H "Accept: application/vnd.github+json" \ 49 + -H "X-GitHub-Api-Version: 2022-11-28" \ 50 + "/repos/$baseRepo/pulls/$prNumber/reviews" \ 51 + --jq '.[].user.login' > "$tmp/already-reviewed-by" 52 + 53 + # And we don't want to rerequest reviews from people who already reviewed 54 + while read -r user; do 55 + if [[ -v users[${user,,}] ]]; then 56 + log "User $user is a potential reviewer, but has already left a review, ignoring" 57 + unset 'users[${user,,}]' 58 + fi 59 + done < "$tmp/already-reviewed-by" 60 + 61 + for user in "${!users[@]}"; do 62 + if ! gh api \ 63 + -H "Accept: application/vnd.github+json" \ 64 + -H "X-GitHub-Api-Version: 2022-11-28" \ 65 + "/repos/$baseRepo/collaborators/$user" >&2; then 66 + log "User $user is not a repository collaborator, probably missed the automated invite to the maintainers team (see <https://github.com/NixOS/nixpkgs/issues/234293>), ignoring" 67 + unset 'users[$user]' 68 + fi 69 + done 70 + 71 + for user in "${!users[@]}"; do 72 + log "Requesting review from: $user" 73 + 74 + if ! response=$(jq -n --arg user "$user" '{ reviewers: [ $user ] }' | \ 75 + effect gh api \ 76 + --method POST \ 77 + -H "Accept: application/vnd.github+json" \ 78 + -H "X-GitHub-Api-Version: 2022-11-28" \ 79 + "/repos/$baseRepo/pulls/$prNumber/requested_reviewers" \ 80 + --input -); then 81 + log "Failed to request review from $user: $response" 82 + fi 83 + done
-96
ci/request-reviews/request-reviews.sh
··· 1 - #!/usr/bin/env bash 2 - 3 - # Requests reviews for a PR after verifying that the base branch is correct 4 - 5 - set -euo pipefail 6 - tmp=$(mktemp -d) 7 - trap 'rm -rf "$tmp"' exit 8 - SCRIPT_DIR=$(dirname "$0") 9 - 10 - log() { 11 - echo "$@" >&2 12 - } 13 - 14 - effect() { 15 - if [[ -n "${DRY_MODE:-}" ]]; then 16 - log "Skipping in dry mode:" "${@@Q}" 17 - else 18 - "$@" 19 - fi 20 - } 21 - 22 - if (( $# < 3 )); then 23 - log "Usage: $0 GITHUB_REPO PR_NUMBER OWNERS_FILE" 24 - exit 1 25 - fi 26 - baseRepo=$1 27 - prNumber=$2 28 - ownersFile=$3 29 - 30 - log "Fetching PR info" 31 - prInfo=$(gh api \ 32 - -H "Accept: application/vnd.github+json" \ 33 - -H "X-GitHub-Api-Version: 2022-11-28" \ 34 - "/repos/$baseRepo/pulls/$prNumber") 35 - 36 - baseBranch=$(jq -r .base.ref <<< "$prInfo") 37 - log "Base branch: $baseBranch" 38 - prRepo=$(jq -r .head.repo.full_name <<< "$prInfo") 39 - log "PR repo: $prRepo" 40 - prBranch=$(jq -r .head.ref <<< "$prInfo") 41 - log "PR branch: $prBranch" 42 - prAuthor=$(jq -r .user.login <<< "$prInfo") 43 - log "PR author: $prAuthor" 44 - 45 - extraArgs=() 46 - if pwdRepo=$(git rev-parse --show-toplevel 2>/dev/null); then 47 - # Speedup for local runs 48 - extraArgs+=(--reference-if-able "$pwdRepo") 49 - fi 50 - 51 - log "Fetching Nixpkgs commit history" 52 - # We only need the commit history, not the contents, so we can do a tree-less clone using tree:0 53 - # https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-quick-summary 54 - git clone --bare --filter=tree:0 --no-tags --origin upstream "${extraArgs[@]}" https://github.com/"$baseRepo".git "$tmp"/nixpkgs.git 55 - 56 - log "Fetching the PR commit history" 57 - # Fetch the PR 58 - git -C "$tmp/nixpkgs.git" remote add fork https://github.com/"$prRepo".git 59 - # This remote config is the same as --filter=tree:0 when cloning 60 - git -C "$tmp/nixpkgs.git" config remote.fork.partialclonefilter tree:0 61 - git -C "$tmp/nixpkgs.git" config remote.fork.promisor true 62 - 63 - git -C "$tmp/nixpkgs.git" fetch --no-tags fork "$prBranch" 64 - headRef=$(git -C "$tmp/nixpkgs.git" rev-parse refs/remotes/fork/"$prBranch") 65 - 66 - log "Checking correctness of the base branch" 67 - if ! "$SCRIPT_DIR"/verify-base-branch.sh "$tmp/nixpkgs.git" "$headRef" "$baseRepo" "$baseBranch" "$prRepo" "$prBranch" | tee "$tmp/invalid-base-error" >&2; then 68 - log "Posting error as comment" 69 - if ! response=$(effect gh api \ 70 - --method POST \ 71 - -H "Accept: application/vnd.github+json" \ 72 - -H "X-GitHub-Api-Version: 2022-11-28" \ 73 - "/repos/$baseRepo/issues/$prNumber/comments" \ 74 - -F "body=@$tmp/invalid-base-error"); then 75 - log "Failed to post the comment: $response" 76 - fi 77 - exit 1 78 - fi 79 - 80 - log "Getting code owners to request reviews from" 81 - "$SCRIPT_DIR"/get-reviewers.sh "$tmp/nixpkgs.git" "$ownersFile" "$baseBranch" "$headRef" | \ 82 - "$SCRIPT_DIR"/process-reviewers.sh "$baseRepo" "$prNumber" "$prAuthor" > "$tmp/reviewers.json" 83 - 84 - log "Requesting reviews from: $(<"$tmp/reviewers.json")" 85 - 86 - if ! response=$(effect gh api \ 87 - --method POST \ 88 - -H "Accept: application/vnd.github+json" \ 89 - -H "X-GitHub-Api-Version: 2022-11-28" \ 90 - "/repos/$baseRepo/pulls/$prNumber/requested_reviewers" \ 91 - --input "$tmp/reviewers.json"); then 92 - log "Failed to request reviews: $response" 93 - exit 1 94 - fi 95 - 96 - log "Successfully requested reviews"