Skip to content

Commit 76b9a2f

Browse files
committed
ci(scan-logs): attach pipeline annotation if concerning logs are found
1 parent 3ce8256 commit 76b9a2f

4 files changed

Lines changed: 238 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,11 @@ jobs:
118118
echo "One or more required jobs failed or was cancelled. Marking finalizer as failed."
119119
exit 1
120120
fi
121+
- name: Checkout the repository
122+
uses: actions/checkout@v4
123+
- name: Scan workflow logs for warnings and errors
124+
run: scripts/scan-workflow-logs.sh ${{ github.run_id }}
125+
env:
126+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
121127
- name: Finalize
122128
run: echo "Pipeline complete!"

scripts/scan_workflow_logs.sh

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Check if run ID is provided as argument
5+
if [[ $# -eq 1 ]]; then
6+
GITHUB_RUN_ID="$1"
7+
elif [[ -z "${GITHUB_RUN_ID:-}" ]]; then
8+
echo "Usage: $0 <run_id>"
9+
echo "Or set GITHUB_RUN_ID environment variable"
10+
exit 1
11+
fi
12+
13+
# Ensure required environment variables
14+
: "${GITHUB_REPOSITORY:?Missing GITHUB_REPOSITORY}"
15+
: "${GITHUB_TOKEN:?Missing GITHUB_TOKEN}"
16+
17+
# Use the GH CLI with auth
18+
export GH_TOKEN="$GITHUB_TOKEN"
19+
20+
# Create a temp dir for logs
21+
tmpdir=$(mktemp -d)
22+
trap 'rm -rf "$tmpdir"' EXIT
23+
24+
cd "$tmpdir"
25+
26+
# Get list of jobs in the run
27+
echo "Fetching jobs for run $GITHUB_RUN_ID..."
28+
jobs_json=$(gh api \
29+
-H "Accept: application/vnd.github+json" \
30+
"/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs" \
31+
--paginate)
32+
33+
# Extract completed job IDs (excluding the current job if GITHUB_JOB is set and always excluding "Test Scripts")
34+
if [[ -n "${GITHUB_JOB:-}" ]]; then
35+
completed_jobs=$(echo "$jobs_json" | jq -r --arg current_job "$GITHUB_JOB" '.jobs[] | select(.status == "completed") | select(.name != $current_job) | select(.name != "Test Scripts") | .id')
36+
else
37+
completed_jobs=$(echo "$jobs_json" | jq -r '.jobs[] | select(.status == "completed") | select(.name != "Test Scripts") | .id')
38+
fi
39+
40+
if [[ -z "$completed_jobs" ]]; then
41+
echo "::warning::No completed jobs found to scan"
42+
exit 0
43+
fi
44+
45+
# Download logs for each completed job
46+
echo "Downloading logs for completed jobs..."
47+
for job_id in $completed_jobs; do
48+
job_name=$(echo "$jobs_json" | jq -r --arg id "$job_id" '.jobs[] | select(.id == ($id | tonumber)) | .name')
49+
echo " Downloading logs for job: $job_name (ID: $job_id)"
50+
51+
# Create directory for this job's logs
52+
mkdir -p "$job_name"
53+
54+
# Download the job's log
55+
if gh api \
56+
-H "Accept: application/vnd.github+json" \
57+
"/repos/${GITHUB_REPOSITORY}/actions/jobs/${job_id}/logs" \
58+
--method GET > "$job_name/log.txt" 2>/dev/null; then
59+
echo " ✓ Downloaded $(wc -l < "$job_name/log.txt") lines"
60+
else
61+
echo " ✗ Failed to download logs"
62+
rm -rf "$job_name"
63+
fi
64+
done
65+
66+
# Scan for lines with "deprecated", "warning:", or "error"
67+
echo "Scanning logs for warnings, deprecations, and errors..."
68+
69+
# Process each log file and collect results
70+
results_file=$(mktemp)
71+
72+
find . -type f -name "*.txt" | while IFS= read -r logfile; do
73+
# Get the job name from the directory structure
74+
job_name=$(dirname "$logfile" | sed 's|^\./||' | cut -d'/' -f1)
75+
76+
# Search for patterns with context
77+
while IFS= read -r line; do
78+
# Extract line number and content
79+
line_num=$(echo "$line" | cut -d: -f2)
80+
content=$(echo "$line" | cut -d: -f3-)
81+
82+
# Determine the type of issue and output both annotation and count
83+
if echo "$content" | grep -qiE '\berror\b'; then
84+
echo "::error file=$job_name,line=$line_num::$content"
85+
echo "error" >> "$results_file"
86+
elif echo "$content" | grep -qiE '\bwarning:'; then
87+
echo "::warning file=$job_name,line=$line_num::$content"
88+
echo "warning" >> "$results_file"
89+
elif echo "$content" | grep -qiE '\bdeprecated\b'; then
90+
echo "::warning file=$job_name,line=$line_num::$content"
91+
echo "deprecated" >> "$results_file"
92+
fi
93+
done < <(grep -niE '(\berror\b|warning:|deprecated)' "$logfile" 2>/dev/null || true)
94+
done
95+
96+
# Count results
97+
error_count=$(grep -c "error" "$results_file" 2>/dev/null || echo 0)
98+
warning_count=$(grep -c "warning" "$results_file" 2>/dev/null || echo 0)
99+
deprecated_count=$(grep -c "deprecated" "$results_file" 2>/dev/null || echo 0)
100+
101+
# Summary
102+
echo
103+
echo "=== Log Scan Summary ==="
104+
echo "Errors found: $error_count"
105+
echo "Warnings found: $warning_count"
106+
echo "Deprecation notices found: $deprecated_count"
107+
echo "Total issues: $((error_count + warning_count + deprecated_count))"
108+
109+
# Clean up temp file
110+
rm -f "$results_file"
111+
112+
# Exit with success even if issues were found
113+
exit 0

{{cookiecutter.project_name|replace(" ", "")}}/.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,11 @@ jobs:
146146
echo "One or more required jobs failed or was cancelled. Marking finalizer as failed."
147147
exit 1
148148
fi
149+
- name: Checkout the repository
150+
uses: actions/checkout@v4
151+
- name: Scan workflow logs for warnings and errors
152+
run: scripts/scan-workflow-logs.sh {% raw %}${{ github.run_id }}{% endraw %}
153+
env:
154+
GITHUB_TOKEN: "{% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}"
149155
- name: Finalize
150156
run: echo "Pipeline complete!"
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Check if run ID is provided as argument
5+
if [[ $# -eq 1 ]]; then
6+
GITHUB_RUN_ID="$1"
7+
elif [[ -z "${GITHUB_RUN_ID:-}" ]]; then
8+
echo "Usage: $0 <run_id>"
9+
echo "Or set GITHUB_RUN_ID environment variable"
10+
exit 1
11+
fi
12+
13+
# Ensure required environment variables
14+
: "${GITHUB_REPOSITORY:?Missing GITHUB_REPOSITORY}"
15+
: "${GITHUB_TOKEN:?Missing GITHUB_TOKEN}"
16+
17+
# Use the GH CLI with auth
18+
export GH_TOKEN="$GITHUB_TOKEN"
19+
20+
# Create a temp dir for logs
21+
tmpdir=$(mktemp -d)
22+
trap 'rm -rf "$tmpdir"' EXIT
23+
24+
cd "$tmpdir"
25+
26+
# Get list of jobs in the run
27+
echo "Fetching jobs for run $GITHUB_RUN_ID..."
28+
jobs_json=$(gh api \
29+
-H "Accept: application/vnd.github+json" \
30+
"/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs" \
31+
--paginate)
32+
33+
# Extract completed job IDs (excluding the current job if GITHUB_JOB is set and always excluding "Test Scripts")
34+
if [[ -n "${GITHUB_JOB:-}" ]]; then
35+
completed_jobs=$(echo "$jobs_json" | jq -r --arg current_job "$GITHUB_JOB" '.jobs[] | select(.status == "completed") | select(.name != $current_job) | select(.name != "Test Scripts") | .id')
36+
else
37+
completed_jobs=$(echo "$jobs_json" | jq -r '.jobs[] | select(.status == "completed") | select(.name != "Test Scripts") | .id')
38+
fi
39+
40+
if [[ -z "$completed_jobs" ]]; then
41+
echo "::warning::No completed jobs found to scan"
42+
exit 0
43+
fi
44+
45+
# Download logs for each completed job
46+
echo "Downloading logs for completed jobs..."
47+
for job_id in $completed_jobs; do
48+
job_name=$(echo "$jobs_json" | jq -r --arg id "$job_id" '.jobs[] | select(.id == ($id | tonumber)) | .name')
49+
echo " Downloading logs for job: $job_name (ID: $job_id)"
50+
51+
# Create directory for this job's logs
52+
mkdir -p "$job_name"
53+
54+
# Download the job's log
55+
if gh api \
56+
-H "Accept: application/vnd.github+json" \
57+
"/repos/${GITHUB_REPOSITORY}/actions/jobs/${job_id}/logs" \
58+
--method GET > "$job_name/log.txt" 2>/dev/null; then
59+
echo " ✓ Downloaded $(wc -l < "$job_name/log.txt") lines"
60+
else
61+
echo " ✗ Failed to download logs"
62+
rm -rf "$job_name"
63+
fi
64+
done
65+
66+
# Scan for lines with "deprecated", "warning:", or "error"
67+
echo "Scanning logs for warnings, deprecations, and errors..."
68+
69+
# Process each log file and collect results
70+
results_file=$(mktemp)
71+
72+
find . -type f -name "*.txt" | while IFS= read -r logfile; do
73+
# Get the job name from the directory structure
74+
job_name=$(dirname "$logfile" | sed 's|^\./||' | cut -d'/' -f1)
75+
76+
# Search for patterns with context
77+
while IFS= read -r line; do
78+
# Extract line number and content
79+
line_num=$(echo "$line" | cut -d: -f2)
80+
content=$(echo "$line" | cut -d: -f3-)
81+
82+
# Determine the type of issue and output both annotation and count
83+
if echo "$content" | grep -qiE '\berror\b'; then
84+
echo "::error file=$job_name,line=$line_num::$content"
85+
echo "error" >> "$results_file"
86+
elif echo "$content" | grep -qiE '\bwarning:'; then
87+
echo "::warning file=$job_name,line=$line_num::$content"
88+
echo "warning" >> "$results_file"
89+
elif echo "$content" | grep -qiE '\bdeprecated\b'; then
90+
echo "::warning file=$job_name,line=$line_num::$content"
91+
echo "deprecated" >> "$results_file"
92+
fi
93+
done < <(grep -niE '(\berror\b|warning:|deprecated)' "$logfile" 2>/dev/null || true)
94+
done
95+
96+
# Count results
97+
error_count=$(grep -c "error" "$results_file" 2>/dev/null || echo 0)
98+
warning_count=$(grep -c "warning" "$results_file" 2>/dev/null || echo 0)
99+
deprecated_count=$(grep -c "deprecated" "$results_file" 2>/dev/null || echo 0)
100+
101+
# Summary
102+
echo
103+
echo "=== Log Scan Summary ==="
104+
echo "Errors found: $error_count"
105+
echo "Warnings found: $warning_count"
106+
echo "Deprecation notices found: $deprecated_count"
107+
echo "Total issues: $((error_count + warning_count + deprecated_count))"
108+
109+
# Clean up temp file
110+
rm -f "$results_file"
111+
112+
# Exit with success even if issues were found
113+
exit 0

0 commit comments

Comments
 (0)