fetch-commit-logs.sh
fetch-commit-logs.shBrowse 3 files
1,205 tokens
3,519 bytes
Token encoding: o200k_base
Snapshot 500c9c8
← Back to SKILL.md
1#!/bin/bash2set -euo pipefail3 4# Usage: fetch-commit-logs.sh [<sha>]5# Fetches CI failure logs for a commit into ~/tmp/commit-<short-sha>/6 7REPO="microsoft/playwright"8REF="${1:-main}"9 10# Resolve commit11COMMIT_JSON=$(gh api "repos/$REPO/commits/$REF" --jq '{sha: .sha, message: .commit.message}')12SHA=$(echo "$COMMIT_JSON" | jq -r '.sha')13SHORT_SHA="${SHA:0:7}"14MESSAGE=$(echo "$COMMIT_JSON" | jq -r '.message' | head -1)15 16OUTDIR="$HOME/tmp/commit-$SHORT_SHA"17mkdir -p "$OUTDIR"18 19echo "Commit: $SHORT_SHA — $MESSAGE"20echo "Output: $OUTDIR"21 22# Get all workflow runs for this commit23RUNS_JSON=$(gh api "repos/$REPO/actions/runs?head_sha=$SHA&per_page=50" \24 --jq '[.workflow_runs[] | {id, name, conclusion, workflow_id}]')25 26# Filter to failed workflows27FAILED_RUNS=$(echo "$RUNS_JSON" | jq -c '[.[] | select(.conclusion == "failure" or .conclusion == null)]')28FAILED_COUNT=$(echo "$FAILED_RUNS" | jq 'length')29 30if [ "$FAILED_COUNT" -eq 0 ]; then31 echo "No failed workflows."32 echo '{"sha":"'"$SHA"'","short_sha":"'"$SHORT_SHA"'","message":"'"$MESSAGE"'","workflows":[]}' | jq . > "$OUTDIR/summary.json"33 echo "$OUTDIR"34 exit 035fi36 37echo "Found $FAILED_COUNT failed workflow(s). Fetching failed jobs..."38 39# Build summary and collect jobs to fetch40SUMMARY='{"sha":"'"$SHA"'","short_sha":"'"$SHORT_SHA"'","message":'"$(echo "$MESSAGE" | jq -Rs .)"',"workflows":[]}'41 42sanitize() {43 echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//'44}45 46PIDS=()47 48for i in $(seq 0 $((FAILED_COUNT - 1))); do49 RUN=$(echo "$FAILED_RUNS" | jq -c ".[$i]")50 RUN_ID=$(echo "$RUN" | jq -r '.id')51 RUN_NAME=$(echo "$RUN" | jq -r '.name')52 WORKFLOW_DIR=$(sanitize "$RUN_NAME")53 54 # Get failed jobs for this run55 JOBS_JSON=$(gh run view "$RUN_ID" --json jobs \56 --jq '[.jobs[] | select(.conclusion == "failure") | {name: .name, id: .databaseId}]')57 JOB_COUNT=$(echo "$JOBS_JSON" | jq 'length')58 59 if [ "$JOB_COUNT" -eq 0 ]; then60 continue61 fi62 63 mkdir -p "$OUTDIR/$WORKFLOW_DIR"64 65 # Add to summary66 WORKFLOW_ENTRY=$(jq -n \67 --arg name "$RUN_NAME" \68 --argjson id "$RUN_ID" \69 --argjson jobs "$JOBS_JSON" \70 '{name: $name, id: $id, failed_jobs: $jobs}')71 SUMMARY=$(echo "$SUMMARY" | jq --argjson w "$WORKFLOW_ENTRY" '.workflows += [$w]')72 73 # Fetch logs in parallel74 for j in $(seq 0 $((JOB_COUNT - 1))); do75 JOB=$(echo "$JOBS_JSON" | jq -c ".[$j]")76 JOB_ID=$(echo "$JOB" | jq -r '.id')77 JOB_NAME=$(echo "$JOB" | jq -r '.name')78 JOB_FILE=$(sanitize "$JOB_NAME")79 LOG_PATH="$OUTDIR/$WORKFLOW_DIR/$JOB_FILE.log"80 81 (82 echo "# $JOB_NAME" > "$LOG_PATH"83 echo "" >> "$LOG_PATH"84 gh run view --job "$JOB_ID" --log-failed 2>&1 | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | sed $'s/^[^\t]*\t[^\t]*\t\xef\xbb\xbf\{0,1\}[0-9T:.Z-]* //' >> "$LOG_PATH" || true85 # If only header (e.g. workflow still in progress), fetch via jobs API86 if [ "$(wc -l < "$LOG_PATH")" -le 3 ]; then87 echo "# $JOB_NAME" > "$LOG_PATH"88 echo "" >> "$LOG_PATH"89 gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" 2>&1 | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | sed $'s/\xef\xbb\xbf//g' | sed 's/^[0-9T:.Z-]* //' >> "$LOG_PATH" || true90 fi91 echo " Fetched: $WORKFLOW_DIR/$JOB_FILE.log"92 ) &93 PIDS+=($!)94 done95done96 97# Wait for all parallel fetches98for pid in "${PIDS[@]}"; do99 wait "$pid" 2>/dev/null || true100done101 102# Write summary103echo "$SUMMARY" | jq . > "$OUTDIR/summary.json"104 105echo ""106echo "Done. Logs saved to: $OUTDIR"107echo "$OUTDIR"108