#!/bin/bash # Pretty display for GitHub PR checks using gum # Usage: ./gh-pr-checks-pretty.sh [--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 [--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