prch/prch.sh
2025-10-17 00:48:09 +02:00

219 lines
6.8 KiB
Bash

#!/bin/bash
# Pretty display for GitHub PR checks using gum
# Usage: ./gh-pr-checks-pretty.sh <PR_NUMBER> [--watch]
set -euo pipefail
# Parse arguments
WATCH_MODE=false
PR_NUMBER=""
while [[ $# -gt 0 ]]; do
case $1 in
--watch|-w)
WATCH_MODE=true
shift
;;
*)
PR_NUMBER=$1
shift
;;
esac
done
# Check if PR number is provided
if [ -z "$PR_NUMBER" ]; then
echo "Usage: $0 <PR_NUMBER> [--watch]"
exit 1
fi
# Check if gum is installed
if ! command -v gum &> /dev/null; then
echo "Error: gum is not installed. Install it from https://github.com/charmbracelet/gum"
exit 1
fi
# Check if gh is installed
if ! command -v gh &> /dev/null; then
echo "Error: gh (GitHub CLI) is not installed."
exit 1
fi
# Function to display checks
display_checks() {
# Get PR details including reviews, comments, and state
PR_INFO=$(gh pr view "$PR_NUMBER" --json number,title,url,state,isDraft,reviews,comments)
PR_TITLE=$(echo "$PR_INFO" | jq -r '.title')
PR_URL=$(echo "$PR_INFO" | jq -r '.url')
PR_STATE=$(echo "$PR_INFO" | jq -r '.state')
PR_DRAFT=$(echo "$PR_INFO" | jq -r '.isDraft')
# Count comments
COMMENT_COUNT=$(echo "$PR_INFO" | jq '.comments | length')
# Get approval status
APPROVED_COUNT=$(echo "$PR_INFO" | jq '[.reviews[] | select(.state == "APPROVED")] | length')
CHANGES_REQUESTED=$(echo "$PR_INFO" | jq '[.reviews[] | select(.state == "CHANGES_REQUESTED")] | length')
# Determine PR status string
if [ "$PR_DRAFT" = "true" ]; then
PR_STATUS="Draft"
STATUS_COLOR="8" # Gray
elif [ "$PR_STATE" = "MERGED" ]; then
PR_STATUS="Merged"
STATUS_COLOR="13" # Purple
elif [ "$PR_STATE" = "CLOSED" ]; then
PR_STATUS="Closed"
STATUS_COLOR="9" # Red
else
PR_STATUS="Open"
STATUS_COLOR="10" # Green
fi
# Determine approval string
if [ "$CHANGES_REQUESTED" -gt 0 ]; then
APPROVAL_STATUS="Changes requested"
APPROVAL_COLOR="9" # Red
elif [ "$APPROVED_COUNT" -gt 0 ]; then
APPROVAL_STATUS="✓ Approved ($APPROVED_COUNT)"
APPROVAL_COLOR="10" # Green
else
APPROVAL_STATUS="No reviews"
APPROVAL_COLOR="8" # Gray
fi
# Get checks with full details
CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --json bucket,name,link,completedAt,description,state)
# Count all checks
TOTAL_ALL=$(echo "$CHECKS_JSON" | jq '. | length')
SUCCESSFUL_ALL=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state == "SUCCESS")] | length')
FAILING_ALL=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state == "FAILURE")] | length')
PENDING_ALL=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state == "PENDING" or .state == "IN_PROGRESS")] | length')
# Filter checks: only include Terraform results, failures, or ongoing checks
FILTERED_JSON=$(echo "$CHECKS_JSON" | jq '[.[] | select(
(.description | contains("Run not triggered") | not) and
(
(.description | contains("Terraform plan:")) or
(.state == "FAILURE") or
(.state == "PENDING") or
(.state == "IN_PROGRESS")
)
)]')
# Count filtered checks
TOTAL=$(echo "$FILTERED_JSON" | jq '. | length')
HIDDEN=$((SUCCESSFUL_ALL - TOTAL))
# Clear screen in watch mode
if [ "$WATCH_MODE" = true ]; then
clear
fi
# Display header
gum style \
--border double \
--border-foreground 212 \
--padding "1 2" \
--margin "1 0" \
--align center \
"#$PR_NUMBER | $PR_TITLE" "$PR_URL"
# Display PR status, approval, and comments
echo ""
gum style --foreground "$STATUS_COLOR" --bold "Status: $PR_STATUS" | tr '\n' ' '
echo -n " | "
gum style --foreground "$APPROVAL_COLOR" "Reviews: $APPROVAL_STATUS" | tr '\n' ' '
echo -n " | "
gum style --foreground 12 "Comments: $COMMENT_COUNT"
# Display summary statistics
echo ""
if [ "$TOTAL" -eq 0 ]; then
gum style --foreground 8 "Total: $TOTAL_ALL | Success: $SUCCESSFUL_ALL | Failed: $FAILING_ALL | Pending: $PENDING_ALL"
echo ""
gum style \
--foreground 10 \
--bold \
"✓ All checks passed"
if [ "$WATCH_MODE" = false ]; then
exit 0
else
return 0
fi
else
gum style --foreground 8 "Total: $TOTAL_ALL | Success: $SUCCESSFUL_ALL ($HIDDEN hidden) | Failed: $FAILING_ALL | Pending: $PENDING_ALL"
fi
echo ""
# Prepare table data
TABLE_DATA=$(echo "$FILTERED_JSON" | jq -r '
[" STATUS", "NAME", "DESCRIPTION", "URL"],
[" ──────", "────", "───────────", "───"],
(.[] | [
" " + (if .state == "SUCCESS" then "✓"
elif .state == "FAILURE" then "✗"
elif .state == "PENDING" or .state == "IN_PROGRESS" then "⏳"
else "○" end),
# Shorten name - extract workspace from Terraform Cloud checks
(if .name | startswith("Terraform Cloud/") then
"TF - " + (.name | split("/") | last)
else
.name
end),
# Format description - compact Terraform plan output
(if .description and (.description | contains("Terraform plan:")) then
(.description |
gsub("Terraform plan: "; "") |
gsub(" to add"; "") |
gsub(" to change"; "") |
gsub(" to destroy\\."; "") |
gsub(", "; " ") |
split(" ") |
"🟢+" + .[0] + " 🛠~" + .[1] + " 🗑-" + .[2])
else
(.description // "No description" |
if (. | length) > 50 then .[0:47] + "..." else . end)
end),
# Full URL
(.link // "")
]) | @tsv
' | column -t -s $'\t')
# Display table with gum
echo "$TABLE_DATA" | gum format
# Show timestamp in watch mode
if [ "$WATCH_MODE" = true ]; then
echo ""
gum style --foreground 8 --italic "Last updated: $(date '+%Y-%m-%d %H:%M:%S')"
fi
}
# Run in watch mode or single run
if [ "$WATCH_MODE" = true ]; then
# Trap Ctrl+C to exit gracefully
trap 'echo ""; echo "Watch mode stopped."; exit 0' INT
while true; do
display_checks
# Check if all checks are complete
CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --json state)
PENDING_COUNT=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state == "PENDING" or .state == "IN_PROGRESS")] | length')
if [ "$PENDING_COUNT" -eq 0 ]; then
echo ""
gum style --foreground 10 --bold "All checks complete!"
break
fi
sleep 10
done
else
display_checks
fi