Skip to content

Instantly share code, notes, and snippets.

@lox
Created May 5, 2025 01:03
Show Gist options
  • Save lox/05ab04c2d1c7b5efe923434ee066bd25 to your computer and use it in GitHub Desktop.
Save lox/05ab04c2d1c7b5efe923434ee066bd25 to your computer and use it in GitHub Desktop.
A shell script for fetching pull request review comments based on current git context
#!/bin/bash
# Script to fetch Pull Request review comments based on the current Git context.
set -euo pipefail # Exit on error, unset variable, or pipe failure
# --- Configuration ---
# Prefer 'origin' remote, fall back to 'upstream'
readonly PRIMARY_REMOTE="origin"
readonly FALLBACK_REMOTE="upstream"
# --- Helper Functions ---
# Print error message to stderr and exit
error_exit() {
echo "Error: $1" >&2
exit 1
}
# Check if a command exists in PATH
check_command() {
local cmd="$1"
if ! command -v "$cmd" &> /dev/null; then
error_exit "Required command '$cmd' is not installed or not found in PATH."
fi
}
# Get the remote URL, preferring primary, falling back to secondary
get_git_remote_url() {
local url
url=$(git config --get remote."$PRIMARY_REMOTE".url || true) # Allow command to fail without exiting script
if [[ -z "$url" ]]; then
echo "Warning: Remote '$PRIMARY_REMOTE' not found, trying '$FALLBACK_REMOTE'..." >&2
url=$(git config --get remote."$FALLBACK_REMOTE".url || true)
fi
if [[ -z "$url" ]]; then
error_exit "Could not determine remote URL (checked '$PRIMARY_REMOTE' and '$FALLBACK_REMOTE')."
fi
echo "$url"
}
# Parse GitHub organization and repository name from a URL
parse_org_repo() {
local url="$1"
local org_repo
# Handles https://, git@, and removes .git suffix
org_repo=$(echo "$url" | sed -E 's/^(https?:\/\/|git@)[^:\/]+[:\/]//; s/\.git$//')
if [[ ! "$org_repo" == */* ]]; then
error_exit "Could not parse organization/repository from remote URL: $url"
fi
echo "$org_repo" # Returns format "org/repo"
}
# Get the current Git branch name
get_current_branch() {
local branch
branch=$(git branch --show-current)
if [[ -z "$branch" ]]; then
error_exit "Could not determine current branch."
fi
echo "$branch"
}
# Find the PR number associated with a branch and repo
# Tries open PRs first, then the most recent closed/merged PR.
find_pr_number() {
local branch="$1"
local org_repo="$2"
local pr_number
echo "--> Searching for open PR associated with branch '$branch'..." >&2
pr_number=$(gh pr list --head "$branch" --repo "$org_repo" --state open --json number --limit 1 --jq '.[0].number // empty' || true)
if [[ -z "$pr_number" ]]; then
echo "--> No open PR found. Searching for most recent closed/merged PR..." >&2
# Use --state all and filter with --search for merged/closed states and the specific head branch
# Also sort by updated date descending to get the most recent one.
pr_number=$(gh pr list --repo "$org_repo" --state all --search "head:\"$branch\" is:merged,closed sort:updated-desc" --json number --limit 1 --jq '.[0].number // empty' || true)
if [[ -z "$pr_number" ]]; then
error_exit "No open, closed, or merged Pull Request found for branch '$branch' in $org_repo."
else
echo "--> Found most recent closed/merged PR: #$pr_number" >&2
fi
else
echo "--> Found open PR Number: #$pr_number" >&2
fi
echo "$pr_number"
}
# Fetch comments for a given PR number
fetch_pr_comments() {
local org_repo="$1"
local pr_number="$2"
local api_path="/repos/$org_repo/pulls/$pr_number/comments"
local output
echo "--> Fetching comments for PR #$pr_number..." >&2
# Try with cache first
output=$(gh api "$api_path" --cache 1h -q '[.[] | {reviewer: .user.login, comment: .body, file: .path, line: (.original_line // .line)}]' 2>/dev/null || true)
# Validate output or retry without cache
if [[ -z "$output" ]] || (! echo "$output" | jq empty > /dev/null 2>&1 ); then
echo "--> Retrying comment fetch without cache..." >&2
output=$(gh api "$api_path" --cache 0 -q '[.[] | {reviewer: .user.login, comment: .body, file: .path, line: (.original_line // .line)}]' 2>/dev/null || true)
# Final validation after retry
if [[ -z "$output" ]] || (! echo "$output" | jq empty > /dev/null 2>&1 ); then
error_exit "Failed to fetch or process comments from GitHub API for PR #$pr_number, even after retry."
fi
fi
echo "$output"
}
# --- Main Script Logic ---
# --- Argument Parsing ---
pr_number_arg=""
if [[ $# -gt 0 ]] && [[ "$1" =~ ^[0-9]+$ ]]; then
pr_number_arg="$1"
echo "--> Using provided PR number: #$pr_number_arg" >&2
fi
# --- Initial Checks ---
check_command "git"
check_command "gh"
check_command "jq"
if ! git rev-parse --is-inside-work-tree &> /dev/null; then
error_exit "Not inside a git repository."
fi
# Detect Context
remote_url=$(get_git_remote_url)
org_repo=$(parse_org_repo "$remote_url")
echo "--> Detected Repository: $org_repo" >&2
# --- Determine PR Number ---
if [[ -n "$pr_number_arg" ]]; then
pr_number="$pr_number_arg"
# Optional: Add a check here to verify the PR exists in the repo?
# gh pr view "$pr_number" --repo "$org_repo" > /dev/null || error_exit "PR #$pr_number not found in $org_repo."
else
# Only detect branch and find PR if number wasn't provided
current_branch=$(get_current_branch)
echo "--> Detected Branch: $current_branch" >&2
pr_number=$(find_pr_number "$current_branch" "$org_repo")
fi
# Fetch and Output Comments
comments_json=$(fetch_pr_comments "$org_repo" "$pr_number")
if [[ "$comments_json" == "[]" ]]; then
echo "--> No review comments found for PR #$pr_number." >&2
else
# Format the output for better readability (e.g., for LLM input)
echo "Review comments for PR #$pr_number in $org_repo:"
echo "---"
echo "$comments_json" | jq -c '.[]' | while IFS= read -r comment_obj; do
file=$(echo "$comment_obj" | jq -r '.file')
line=$(echo "$comment_obj" | jq -r '.line')
reviewer=$(echo "$comment_obj" | jq -r '.reviewer')
comment_body=$(echo "$comment_obj" | jq -r '.comment')
echo "File: $file"
echo "Line: $line"
echo "Reviewer: $reviewer"
echo "Comment:"
# Indent comment body slightly for clarity
# Use parameter expansion or printf instead of sed for simple substitution
while IFS= read -r line; do
printf ' %s\n' "$line"
done <<< "$comment_body"
echo "---"
done
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment