Last active
October 6, 2025 16:58
-
-
Save jtyr/96aa214ce0719d9e5f1449d11fc1764c to your computer and use it in GitHub Desktop.
GitHub PR waiting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Exit on any error | |
| set -o errexit -o pipefail -o noclobber -o nounset | |
| function msg() { | |
| local TYPE TEXT EXIT | |
| TYPE="$1" | |
| TEXT="$2" | |
| EXIT="${3:-}" | |
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] $TYPE: $TEXT" | |
| if [[ -n $EXIT ]]; then | |
| exit "$EXIT" | |
| fi | |
| } | |
| function usage() { | |
| local EXIT | |
| EXIT="${1:-0}" | |
| echo "Usage: $0 [-a] [--help]" | |
| echo '' | |
| echo 'Options:' | |
| echo ' -a, --ignore-approvals Do not wait for approvals' | |
| echo ' --help Show this helm message' | |
| echo '' | |
| echo 'Examples:' | |
| echo '# Create PR, wait for all checks and approvals and then merge it' | |
| echo "gh pr create -f && $0 && gh pr merge -sd" | |
| echo '# Create PR, wait for all checks, skip approvals and then merge it' | |
| echo "gh pr create -f && $0 -a && gh pr merge -sd --admin" | |
| exit "$EXIT" | |
| } | |
| function main() { | |
| local LONGOPTS OPTIONS PARSED IGNORE_APPROVALS SHOW_USAGE | |
| # Define long and short options | |
| LONGOPTS='ignore-approvals,help' | |
| OPTIONS='ah' | |
| # Parse options | |
| PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "${0##*/}" -- "$@"; echo $?) | |
| # shellcheck disable=SC2206 | |
| PARSED=($PARSED) | |
| # Check if getopt failed | |
| if [[ ${#PARSED[@]} -lt 2 ]] || [[ ${PARSED[-1]} != 0 ]]; then | |
| msg 'E' 'Execution failed' | |
| usage 1 | |
| fi | |
| # Re-read parsed options | |
| set -- "${PARSED[@]:0:${#PARSED[@]}}" | |
| # Default values | |
| IGNORE_APPROVALS=0 | |
| SHOW_USAGE=0 | |
| # Process individual options | |
| while true; do | |
| case "$1" in | |
| -a|--ignore-approvals) | |
| IGNORE_APPROVALS=1 | |
| shift | |
| ;; | |
| --help) | |
| SHOW_USAGE=1 | |
| shift | |
| ;; | |
| --) | |
| shift | |
| break | |
| ;; | |
| *) | |
| msg 'E' 'This should never happened' | |
| usage 3 | |
| ;; | |
| esac | |
| done | |
| # Show the help message and exit | |
| if [[ $SHOW_USAGE == 1 ]]; then | |
| usage | |
| fi | |
| # Wait for the PR to be created | |
| while [[ $(gh pr checks 2>&1 | grep -c 'no pull requests found') -gt 0 ]]; do | |
| msg 'I' 'Waiting for PR to be created' | |
| sleep 5 | |
| done | |
| # Wait for the checks to start running | |
| while [[ $(gh pr checks 2>&1 | grep -c 'no checks reported') -gt 0 ]]; do | |
| msg 'I' 'Waiting for checks to start running' | |
| sleep 5 | |
| done | |
| # Wait for the checks to finish | |
| while [[ $(gh pr checks | cut -f2 | grep -c 'pending') -gt 0 ]]; do | |
| msg 'I' 'Waiting for checks to finish' | |
| sleep 10 | |
| done | |
| # Check if there is any check that failed | |
| if [[ $(gh pr checks | cut -f2 | grep -Pcv '(pass|skipping)') -gt 0 ]]; then | |
| msg 'E' 'PR checks failed' 1 | |
| fi | |
| if [[ $IGNORE_APPROVALS == 0 ]]; then | |
| local REVIEW_DECISION OWNER REPO PR_NUMBER GRAPH_QUERY UNRESOLVED_COMMENTS | |
| REVIEW_DECISION=$(gh pr status --json reviewDecision --jq '.currentBranch.reviewDecision') | |
| OWNER=$(gh repo view --json owner --jq '.owner.login') | |
| REPO=$(gh repo view --json name --jq '.name') | |
| PR_NUMBER=$(gh pr view --json number --jq '.number') | |
| GRAPH_QUERY="{repository(owner:\"$OWNER\",name:\"$REPO\"){pullRequest(number:$PR_NUMBER){reviewThreads(first:100){nodes{isResolved comments(first:1){nodes{author{login}}}}}}}}" | |
| UNRESOLVED_COMMENTS=$(gh api graphql -f query="$GRAPH_QUERY" --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .comments.nodes[0].author.login != "github-advanced-security")] | length') | |
| # Wait for approvals | |
| while [[ $REVIEW_DECISION =~ ^(REVIEW_REQUIRED|CHANGES_REQUESTED)$ ]] || [[ $UNRESOLVED_COMMENTS -ne 0 ]]; do | |
| if [[ $(gh pr status --json state --jq '.currentBranch.state') == 'CLOSED' ]]; then | |
| msg 'E' 'PR was closed' 1 | |
| fi | |
| if [[ $REVIEW_DECISION == 'REVIEW_REQUIRED' ]]; then | |
| msg 'I' 'Waiting for approvals' | |
| elif [[ $REVIEW_DECISION == 'CHANGES_REQUESTED' ]]; then | |
| msg 'I' 'Waiting for approvals - changes requested' | |
| fi | |
| if [[ $UNRESOLVED_COMMENTS -ne 0 ]]; then | |
| msg 'I' "Waiting for comments to be resolved ($UNRESOLVED_COMMENTS)" | |
| fi | |
| sleep 10 | |
| REVIEW_DECISION=$(gh pr status --json reviewDecision --jq '.currentBranch.reviewDecision') | |
| UNRESOLVED_COMMENTS=$(gh api graphql -f query="$GRAPH_QUERY" --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .comments.nodes[0].author.login != "github-advanced-security")] | length') | |
| done | |
| fi | |
| msg 'I' 'PR can be merged now' | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment