-
Notifications
You must be signed in to change notification settings - Fork 263
fix(ci-doctor): pre-download logs and artifacts, apply generic error heuristics to reduce token usage #17719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -50,6 +50,110 @@ tools: | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| timeout-minutes: 20 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| steps: | ||||||||||||||||||||||
| - name: Pre-download CI failure logs and artifacts, apply heuristics | ||||||||||||||||||||||
| env: | ||||||||||||||||||||||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||||||||||||||||||
| RUN_ID: ${{ github.event.workflow_run.id }} | ||||||||||||||||||||||
| REPO: ${{ github.repository }} | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| set -e | ||||||||||||||||||||||
| LOG_DIR="/tmp/ci-doctor/logs" | ||||||||||||||||||||||
| ARTIFACT_DIR="/tmp/ci-doctor/artifacts" | ||||||||||||||||||||||
| FILTERED_DIR="/tmp/ci-doctor/filtered" | ||||||||||||||||||||||
| mkdir -p "$LOG_DIR" "$ARTIFACT_DIR" "$FILTERED_DIR" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| echo "=== CI Doctor: Pre-downloading logs and artifacts for run $RUN_ID ===" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Get failed jobs and their failed steps | ||||||||||||||||||||||
| gh api "repos/$REPO/actions/runs/$RUN_ID/jobs" \ | ||||||||||||||||||||||
| --jq '[.jobs[] | select(.conclusion == "failed" or .conclusion == "cancelled") | {id:.id, name:.name, failed_steps:[.steps[]? | select(.conclusion=="failed") | .name]}]' \ | ||||||||||||||||||||||
| > "$LOG_DIR/failed-jobs.json" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| FAILED_COUNT=$(jq 'length' "$LOG_DIR/failed-jobs.json") | ||||||||||||||||||||||
| echo "Found $FAILED_COUNT failed job(s)" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if [ "$FAILED_COUNT" -eq 0 ]; then | ||||||||||||||||||||||
| echo "No failed jobs found, skipping log download" | ||||||||||||||||||||||
| exit 0 | ||||||||||||||||||||||
| fi | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| echo "Failed jobs:" | ||||||||||||||||||||||
| cat "$LOG_DIR/failed-jobs.json" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Download logs for each failed job and apply generic error heuristics | ||||||||||||||||||||||
| jq -r '.[].id' "$LOG_DIR/failed-jobs.json" | while read -r JOB_ID; do | ||||||||||||||||||||||
| LOG_FILE="$LOG_DIR/job-${JOB_ID}.log" | ||||||||||||||||||||||
| echo "Downloading log for job $JOB_ID..." | ||||||||||||||||||||||
| gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" > "$LOG_FILE" 2>/dev/null \ | ||||||||||||||||||||||
| || echo "(log download failed)" > "$LOG_FILE" | ||||||||||||||||||||||
| echo " -> Saved $(wc -l < "$LOG_FILE") lines to $LOG_FILE" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Apply generic heuristics: find lines with common error indicators | ||||||||||||||||||||||
| HINTS_FILE="$FILTERED_DIR/job-${JOB_ID}-hints.txt" | ||||||||||||||||||||||
| grep -n -iE "(error[: ]|ERROR|FAIL|panic:|fatal[: ]|undefined[: ]|exception|exit status [^0])" \ | ||||||||||||||||||||||
| "$LOG_FILE" | head -30 > "$HINTS_FILE" 2>/dev/null || true | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if [ -s "$HINTS_FILE" ]; then | ||||||||||||||||||||||
| echo " -> Pre-located $(wc -l < "$HINTS_FILE") hint line(s) in $HINTS_FILE" | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| echo " -> No error hints found in $LOG_FILE" | ||||||||||||||||||||||
| fi | ||||||||||||||||||||||
| done | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Download and unpack all artifacts from the failed run | ||||||||||||||||||||||
| echo "" | ||||||||||||||||||||||
| echo "=== Downloading artifacts for run $RUN_ID ===" | ||||||||||||||||||||||
| gh run download "$RUN_ID" --repo "$REPO" --dir "$ARTIFACT_DIR" 2>/dev/null \ | ||||||||||||||||||||||
| || echo "No artifacts available or download failed" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Apply heuristics to artifact text files | ||||||||||||||||||||||
| find "$ARTIFACT_DIR" -type f \( \ | ||||||||||||||||||||||
| -name "*.txt" -o -name "*.log" -o -name "*.json" \ | ||||||||||||||||||||||
| -o -name "*.xml" -o -name "*.out" -o -name "*.err" \ | ||||||||||||||||||||||
| \) | while read -r ARTIFACT_FILE; do | ||||||||||||||||||||||
| REL_PATH="${ARTIFACT_FILE#$ARTIFACT_DIR/}" | ||||||||||||||||||||||
|
||||||||||||||||||||||
| REL_PATH="${ARTIFACT_FILE#$ARTIFACT_DIR/}" | |
| REL_PATH="${ARTIFACT_FILE#"$ARTIFACT_DIR"/}" |
Copilot
AI
Feb 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The find command on line 142 in the summary section could fail or produce unexpected output if $ARTIFACT_DIR is empty or doesn't exist. While the directory is created earlier, if artifact download fails, the find might still print an error to stderr. Consider adding a check: [ -d "$ARTIFACT_DIR" ] && find "$ARTIFACT_DIR" -type f || echo " (no artifacts downloaded)" to handle this edge case gracefully.
| find "$ARTIFACT_DIR" -type f | while read -r f; do | |
| echo " $f" | |
| done | |
| if [ -d "$ARTIFACT_DIR" ]; then | |
| find "$ARTIFACT_DIR" -type f | while read -r f; do | |
| echo " $f" | |
| done | |
| else | |
| echo " (no artifacts downloaded)" | |
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The grep pattern
exit status [^0]will match "exit status 1" but also "exit status " followed by any non-zero character (including letters, symbols, etc.), not just non-zero exit codes. The pattern should beexit status [1-9]to match only non-zero numeric exit codes, orexit (code|status) [1-9]to cover both common patterns.This issue also appears on line 118 of the same file.