Skip to content

Instantly share code, notes, and snippets.

@jeromepl
Last active April 1, 2022 15:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeromepl/02e70f3ea4a4e8103da6f96f14eb213c to your computer and use it in GitHub Desktop.
Save jeromepl/02e70f3ea4a4e8103da6f96f14eb213c to your computer and use it in GitHub Desktop.
Mark as "Ready for review" and assign reviewers on GitHub pull requests after test suite passes
#!/bin/bash
set -eou pipefail
# This script was partially inspired by https://arturdryomov.dev/posts/auto-github-pull-requests/
# Few notes about some of the decisions that led to the code below:
# - The GitHub REST API does not support the marking PRs as ready for review.
# Instead, this has to be done through the GraphQL API, **and** requires a
# personal access token since the Github Action default token cannot have
# the required permission
# - It would have been cleaner to split up all the queries in this file as
# different "steps" in the workflow file. However, I found that to be
# massively more painful to test and debug, as you cannot run the whole
# flow locally easily. Having it all in this single bash script makes it
# really easy to test. Simply generate a personal access token in your
# GitHub account settings!
# - The only dependency is currently `jq`, to help with parsing the JSON
# output of GitHub API queries. Note that we could consider also using
# `gh` (the GitHub cli) in the future as it could simplify this code
# quite a bit
TOKEN="${1}"
PR_NUMBERS="${2}"
LABEL="autoready"
REPO="your-repository"
ORGANIZATION="potloc"
# Split the numbers string (comma-delimited)
for pr_number in $(echo $PR_NUMBERS | tr "," "\n"); do
# Get the node_id (and labels) from the PR number
# - https://docs.github.com/en/graphql/guides/using-global-node-ids
# - https://docs.github.com/en/rest/reference/pulls#get-a-pull-request
out=$(curl \
--fail \
--silent \
--show-error \
--header "Accept: application/vnd.github.v3+json" \
--header "Authorization: token ${TOKEN}" \
--request "GET" \
--url "https://api.github.com/repos/${ORGANIZATION}/${REPO}/pulls/${pr_number}"
)
node_id=$(jq -r '.node_id' <<< $out)
contains_label=$(jq "any(.labels[].name == \"${LABEL}\"; .)" <<< $out)
comments_url=$(jq -r ".comments_url" <<< $out)
# Check if the PR contains the label we want
if [ "$contains_label" == "true" ]; then
# Mark the PR as ready for review
curl \
--fail \
--silent \
--show-error \
--header "Content-Type: application/json" \
--header "Authorization: token ${TOKEN}" \
--request "POST" \
--data "{ \"query\": \"mutation { markPullRequestReadyForReview(input: { pullRequestId: \\\"${node_id}\\\" }) { pullRequest { id } } }\" }" \
--url https://api.github.com/graphql
# Remove the label
curl \
--request "DELETE" \
--header "Accept: application/vnd.github.v3+json" \
--header "Authorization: token ${TOKEN}" \
--url "https://api.github.com/repos/${ORGANIZATION}/${REPO}/issues/${pr_number}/labels/${LABEL}"
# Get the comments on the PR
comments_out=$(curl \
--fail \
--silent \
--show-error \
--header "Content-Type: application/vnd.github.v3+json" \
--header "Authorization: token ${TOKEN}" \
--request "GET" \
--url $comments_url)
# Look for a comment matching the 'autoready-reviewers: ' pattern
# If found, assign the mentionned reviewers to review this PR
jq -r ".[].body" <<< $comments_out | while IFS='' read comment; do
if [[ $comment =~ autoready-reviewers:[[:space:]]([a-zA-Z0-9,\-\/]+) ]]; then
all_reviewers=${BASH_REMATCH[1]}
# Split the reviewers between teams and individuals
reviewers_array=()
team_reviewers_array=()
for reviewer in $(echo $all_reviewers | tr "," "\n"); do
if [[ $reviewer =~ [a-zA-Z0-9,\-]+\/[a-zA-Z0-9,\-]+ ]]; then
# In the case of a team reviewer, only take the part of the username after the '/':
slug_array=(${reviewer//\// })
team_slug=${slug_array[1]}
team_reviewers_array+=("\"$team_slug\"")
else
reviewers_array+=("\"$reviewer\"")
fi
done
# Join the array elements into a single comma-separated string:
reviewers=$(IFS=, ; echo "${reviewers_array[*]}")
team_reviewers=$(IFS=, ; echo "${team_reviewers_array[*]}")
# Assign reviewers
curl \
--fail \
--silent \
--show-error \
--output /dev/null \
--header "Accept: application/vnd.github.v3+json" \
--header "Authorization: token ${TOKEN}" \
--request "POST" \
--url "https://api.github.com/repos/${ORGANIZATION}/${REPO}/pulls/${pr_number}/requested_reviewers" \
--data "{\"reviewers\":[${reviewers}], \"team_reviewers\":[${team_reviewers}]}"
break
fi
done
fi
done
name: Ready For Review
on:
workflow_run:
workflows: ["Test"] # Change this to the name of your own workflow
branches-ignore: [main]
types:
- completed
jobs:
mark_as_ready_for_review:
runs-on: self-hosted
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Mark as Ready for Review
run: bash .github/workflows/mark_as_ready_for_review.sh "${{ secrets.ACCESS_TOKEN }}" "${{ join(github.event.workflow_run.pull_requests.*.number) }}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment