Skip to content

Instantly share code, notes, and snippets.

@jtyr
Last active October 6, 2025 16:58
Show Gist options
  • Save jtyr/96aa214ce0719d9e5f1449d11fc1764c to your computer and use it in GitHub Desktop.
Save jtyr/96aa214ce0719d9e5f1449d11fc1764c to your computer and use it in GitHub Desktop.
GitHub PR waiting
#!/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