From a368e6f313fd56f6c1dd61ae58341e01205bdfad Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 29 Oct 2025 00:35:55 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A4=96=20ci:=20improve=20extract=5Fpr?= =?UTF-8?q?=5Flogs=20to=20show=20in-progress=20job=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/extract_pr_logs.sh | 119 +++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 43 deletions(-) diff --git a/scripts/extract_pr_logs.sh b/scripts/extract_pr_logs.sh index 3d9cfe49d..3bc149a21 100755 --- a/scripts/extract_pr_logs.sh +++ b/scripts/extract_pr_logs.sh @@ -1,33 +1,38 @@ #!/usr/bin/env bash -# Extract logs from failed GitHub Actions runs for a PR -# Usage: ./scripts/extract_pr_logs.sh [job_name_pattern] [--wait] +# Extract logs from GitHub Actions runs for a PR (including in-progress jobs) +# Usage: ./scripts/extract_pr_logs.sh [job_name_pattern] [--all] # # Examples: # ./scripts/extract_pr_logs.sh 329 # Latest failed run for PR #329 # ./scripts/extract_pr_logs.sh 329 Integration # Only Integration Test jobs -# ./scripts/extract_pr_logs.sh 329 --wait # Wait for logs to be available +# ./scripts/extract_pr_logs.sh 329 --all # Show all jobs (not just failed) # ./scripts/extract_pr_logs.sh 18640062283 # Specific run ID set -euo pipefail INPUT="${1:-}" JOB_PATTERN="${2:-}" -WAIT_FOR_LOGS=false +SHOW_ALL_JOBS=false # Parse flags -if [[ "$JOB_PATTERN" == "--wait" ]]; then - WAIT_FOR_LOGS=true +for arg in "$@"; do + if [[ "$arg" == "--all" ]]; then + SHOW_ALL_JOBS=true + fi +done + +# Remove flags from JOB_PATTERN if they were set as second arg +if [[ "$JOB_PATTERN" == "--all" ]]; then JOB_PATTERN="" -elif [[ "${3:-}" == "--wait" ]]; then - WAIT_FOR_LOGS=true fi if [[ -z "$INPUT" ]]; then - echo "❌ Usage: $0 [job_name_pattern]" >&2 + echo "❌ Usage: $0 [job_name_pattern] [--all]" >&2 echo "" >&2 echo "Examples:" >&2 - echo " $0 329 # Latest failed run for PR #329 (RECOMMENDED)" >&2 + echo " $0 329 # Latest failed run for PR #329" >&2 echo " $0 329 Integration # Only Integration Test jobs from PR #329" >&2 + echo " $0 329 --all # Show all jobs (not just failed/in-progress)" >&2 echo " $0 18640062283 # Specific run ID" >&2 exit 1 fi @@ -35,20 +40,28 @@ fi # Detect if input is PR number or run ID (run IDs are much longer) if [[ "$INPUT" =~ ^[0-9]{1,5}$ ]]; then PR_NUMBER="$INPUT" - echo "🔍 Finding latest failed run for PR #$PR_NUMBER..." >&2 - - # Get the latest failed run for this PR - RUN_ID=$(gh pr checks "$PR_NUMBER" --json name,link,state --jq '.[] | select(.state == "FAILURE") | .link' | head -1 | sed -E 's|.*/runs/([0-9]+).*|\1|' || echo "") + + # If --all flag is set or no failures, get latest run regardless of status + if [[ "$SHOW_ALL_JOBS" == true ]]; then + echo "🔍 Finding latest run for PR #$PR_NUMBER..." >&2 + RUN_ID=$(gh pr checks "$PR_NUMBER" --json name,link,state --jq '.[] | select(.link | contains("/runs/")) | .link' | head -1 | sed -E 's|.*/runs/([0-9]+).*|\1|' || echo "") + else + echo "🔍 Finding latest failed run for PR #$PR_NUMBER..." >&2 + # Get the latest failed run for this PR + RUN_ID=$(gh pr checks "$PR_NUMBER" --json name,link,state --jq '.[] | select(.state == "FAILURE") | select(.link | contains("/runs/")) | .link' | head -1 | sed -E 's|.*/runs/([0-9]+).*|\1|' || echo "") + fi if [[ -z "$RUN_ID" ]]; then echo "❌ No failed runs found for PR #$PR_NUMBER" >&2 echo "" >&2 echo "Current check status:" >&2 gh pr checks "$PR_NUMBER" 2>&1 || true + echo "" >&2 + echo "💡 Tip: Use --all flag to see logs from any run (not just failed)" >&2 exit 1 fi - echo "📋 Found failed run: $RUN_ID" >&2 + echo "📋 Found run: $RUN_ID" >&2 else RUN_ID="$INPUT" echo "📋 Fetching logs for run $RUN_ID..." >&2 @@ -65,12 +78,18 @@ if [[ -z "$JOBS" ]]; then exit 1 fi -# Filter to failed jobs only (unless specific pattern requested) -if [[ -z "$JOB_PATTERN" ]]; then - FAILED_JOBS=$(echo "$JOBS" | jq -r 'select(.conclusion == "FAILURE" or .conclusion == "TIMED_OUT" or .conclusion == "CANCELLED")') +# Filter jobs based on flags and pattern +if [[ -z "$JOB_PATTERN" ]] && [[ "$SHOW_ALL_JOBS" == false ]]; then + # Show failed/timed out/cancelled jobs, OR in-progress/pending jobs if no failures exist + FAILED_JOBS=$(echo "$JOBS" | jq -r 'select(.conclusion == "failure" or .conclusion == "timed_out" or .conclusion == "cancelled")') + IN_PROGRESS_JOBS=$(echo "$JOBS" | jq -r 'select(.status == "in_progress" or .status == "queued" or .status == "pending")') + if [[ -n "$FAILED_JOBS" ]]; then - echo "đŸŽ¯ Showing only failed jobs (use job_pattern to see others)" >&2 + echo "đŸŽ¯ Showing only failed jobs (use --all to see all jobs)" >&2 JOBS="$FAILED_JOBS" + elif [[ -n "$IN_PROGRESS_JOBS" ]]; then + echo "âŗ No failures yet - showing in-progress/pending jobs (use --all to see all)" >&2 + JOBS="$IN_PROGRESS_JOBS" fi fi @@ -111,43 +130,57 @@ suggest_local_command() { esac } +# Show step-by-step progress for in-progress/pending jobs +show_job_steps() { + local job_id="$1" + local job_status="$2" + + if [[ "$job_status" == "in_progress" ]] || [[ "$job_status" == "queued" ]] || [[ "$job_status" == "pending" ]]; then + echo "" >&2 + echo "📊 Step-by-step status:" >&2 + gh api "/repos/coder/cmux/actions/jobs/$job_id" | jq -r '.steps[] | " [\(.status | ascii_upcase)] \(.name)\(if .conclusion then " (\(.conclusion))" else "" end)"' >&2 + echo "" >&2 + fi +} + # Extract and display logs for each job for JOB_ID in $JOB_IDS; do JOB_INFO=$(echo "$JOBS" | jq -r "select(.databaseId == $JOB_ID)") JOB_NAME=$(echo "$JOB_INFO" | jq -r '.name') - JOB_STATUS=$(echo "$JOB_INFO" | jq -r '.conclusion // .status') + JOB_STATUS=$(echo "$JOB_INFO" | jq -r '.status') + JOB_CONCLUSION=$(echo "$JOB_INFO" | jq -r '.conclusion // "N/A"') + + # Display status: show conclusion if completed, otherwise show status + if [[ "$JOB_STATUS" == "completed" ]]; then + DISPLAY_STATUS="$JOB_CONCLUSION" + else + DISPLAY_STATUS="$JOB_STATUS" + fi echo "" >&2 echo "════════════════════════════════════════════════════════════" >&2 - echo "Job: $JOB_NAME (ID: $JOB_ID) - $JOB_STATUS" >&2 + echo "Job: $JOB_NAME (ID: $JOB_ID)" >&2 + echo "Status: $DISPLAY_STATUS" >&2 echo "════════════════════════════════════════════════════════════" >&2 # Suggest local reproduction command suggest_local_command "$JOB_NAME" >&2 - echo "" >&2 - - # Fetch logs with retry logic if --wait flag is set - MAX_RETRIES=3 - RETRY_COUNT=0 + + # Show step-by-step status for in-progress/pending jobs + show_job_steps "$JOB_ID" "$JOB_STATUS" - while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - # Use gh api to fetch logs (works for individual completed jobs even if run is in progress) + # Try to fetch logs + if [[ "$JOB_STATUS" == "completed" ]]; then + # Completed job - logs should be available if gh api "/repos/coder/cmux/actions/jobs/$JOB_ID/logs" 2>/dev/null; then - break + echo "" >&2 else - RETRY_COUNT=$((RETRY_COUNT + 1)) - if [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ "$WAIT_FOR_LOGS" = true ]; then - echo "âŗ Logs not ready yet, waiting 5 seconds... (attempt $RETRY_COUNT/$MAX_RETRIES)" >&2 - sleep 5 - else - echo "âš ī¸ Could not fetch logs for job $JOB_ID" >&2 - if [ "$WAIT_FOR_LOGS" = false ]; then - echo " Tip: Use --wait flag to retry if logs are still processing" >&2 - else - echo " (logs may have expired or are still processing)" >&2 - fi - break - fi + echo "âš ī¸ Could not fetch logs for completed job $JOB_ID (logs may have expired)" >&2 + echo "" >&2 fi - done + else + # In-progress/pending/queued job - logs not yet available + echo "â„šī¸ Job is $JOB_STATUS - logs will be available when job completes" >&2 + echo "" >&2 + fi done From 508d4b479c24d4f73e0caa225bb9b5390d10e1e2 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 29 Oct 2025 00:57:59 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A4=96=20ci:=20improve=20extract=5Fpr?= =?UTF-8?q?=5Flogs=20to=20show=20in-progress=20job=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shows step-by-step status for in-progress and pending jobs, making it much easier to debug CI runs that haven't completed yet. **Key improvements:** - Shows detailed step status (COMPLETED, IN_PROGRESS, PENDING) for jobs that are still running - Highlights which step is currently running - Provides direct link to view live logs in browser (GitHub API doesn't expose logs for in-progress jobs) - Automatically detects in-progress jobs when no failures exist - Simplified UX: removed --wait flag, pending jobs work automatically - New --all flag to show all jobs regardless of status - Better filtering logic to prioritize failed jobs, then in-progress jobs **Example output for in-progress job:** ``` 📊 Step-by-step status: [COMPLETED] Set up job (success) [COMPLETED] Checkout code (success) [IN_PROGRESS] Run /./.github/actions/setup-cmux [PENDING] Build application 🔄 Currently running: Run /./.github/actions/setup-cmux đŸ‘ī¸ View live logs: https://github.com/coder/cmux/actions/runs/.../job/... ``` _Generated with `cmux`_ --- scripts/extract_pr_logs.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/extract_pr_logs.sh b/scripts/extract_pr_logs.sh index 3bc149a21..e34c21fd6 100755 --- a/scripts/extract_pr_logs.sh +++ b/scripts/extract_pr_logs.sh @@ -179,8 +179,18 @@ for JOB_ID in $JOB_IDS; do echo "" >&2 fi else - # In-progress/pending/queued job - logs not yet available - echo "â„šī¸ Job is $JOB_STATUS - logs will be available when job completes" >&2 + # In-progress/pending/queued job - GitHub API doesn't provide logs until completion + echo "â„šī¸ Job is $JOB_STATUS - logs not available via API until completion" >&2 + echo "" >&2 + + # Show which step is currently running + CURRENT_STEP=$(gh api "/repos/coder/cmux/actions/jobs/$JOB_ID" 2>/dev/null | jq -r '.steps[] | select(.status == "in_progress") | .name' | head -1) + if [[ -n "$CURRENT_STEP" ]]; then + echo "🔄 Currently running: $CURRENT_STEP" >&2 + fi + + # Construct GitHub URL for viewing live logs in browser + echo "đŸ‘ī¸ View live logs: https://github.com/coder/cmux/actions/runs/$RUN_ID/job/$JOB_ID" >&2 echo "" >&2 fi done