Skip to content

Commit a1fc8a9

Browse files
Copilotmmcky
andauthored
Add rate limit handling, API robustness improvements, fix URL construction and bash string concatenation bugs, resolve issue content delivery failures, and add total current issues column to weekly-report action (#209)
* Initial plan * Fix weekly-report jq error when API returns non-array responses Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Add rate limit handling and API delay options to weekly-report action Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix URL construction bug causing 422 errors for endpoints with query parameters Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix issue content empty bug in weekly-report workflow by properly escaping markdown content Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix issue content bug: remove incorrect JSON.parse() call in GitHub script action Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix bash string concatenation in weekly-report action for proper output handling Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Add comprehensive debugging to weekly-report action to diagnose null output issue Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix GITHUB_OUTPUT multiline content handling with unique delimiter to prevent conflicts Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Fix issue content delivery by reading markdown file directly instead of using step outputs Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Remove accidentally committed test file Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> * Add total current issues column to weekly report table Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com>
1 parent a4a4ea5 commit a1fc8a9

File tree

4 files changed

+229
-37
lines changed

4 files changed

+229
-37
lines changed

.github/actions/weekly-report/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ This action generates a report containing:
1414
- **Smart repository filtering**: Uses GitHub Search API to identify repositories with recent activity (commits in the last 7 days) before checking for issues and PRs
1515
- **Fallback mechanism**: If no repositories are found with recent commits, falls back to checking all organization repositories to ensure complete coverage
1616
- **Activity-based reporting**: Only includes repositories with actual activity in the generated report
17+
- **Rate limit handling**: Automatically retries on rate limit errors with exponential backoff, and provides clear warnings when data is incomplete
18+
- **Configurable delays**: Optional delays between API calls to reduce rate limit pressure
1719

1820
## Usage
1921

@@ -25,6 +27,7 @@ This action generates a report containing:
2527
organization: 'QuantEcon'
2628
output-format: 'markdown'
2729
exclude-repos: 'lecture-python.notebooks,auto-updated-repo'
30+
api-delay: '1' # Add 1 second delay between API calls to avoid rate limits
2831
```
2932
3033
## Inputs
@@ -35,6 +38,7 @@ This action generates a report containing:
3538
| `organization` | GitHub organization name | No | `QuantEcon` |
3639
| `output-format` | Output format (`markdown` or `json`) | No | `markdown` |
3740
| `exclude-repos` | Comma-separated list of repository names to exclude from the report | No | `''` |
41+
| `api-delay` | Delay in seconds between API calls to avoid rate limits (0 = no delay) | No | `0` |
3842

3943
## Outputs
4044

@@ -59,6 +63,16 @@ See the [weekly report workflow](../../workflows/weekly-report.yml) for a comple
5963
The generated markdown report includes:
6064
- A summary table showing activity by repository
6165
- Total counts across all repositories
66+
- Data completeness warnings if API calls failed due to rate limits or other errors
6267
- Report metadata (generation date, period covered)
6368

64-
Only repositories with activity in the reporting period are included in the detailed table.
69+
Only repositories with activity in the reporting period are included in the detailed table.
70+
71+
## Rate Limiting
72+
73+
GitHub's API has rate limits (5000 requests/hour for authenticated requests). For large organizations:
74+
75+
- **Monitor warnings**: The report will include warnings when rate limits are hit
76+
- **Add delays**: Use the `api-delay` parameter to add delays between requests (e.g., `api-delay: '1'` for 1 second delays)
77+
- **Run during off-peak**: Schedule reports during off-peak hours to avoid conflicts with other API usage
78+
- **Incomplete data**: When rate limited, the report will show `0` for affected repositories and include a warning

.github/actions/weekly-report/action.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ inputs:
1818
description: 'Comma-separated list of repository names to exclude from the report'
1919
required: false
2020
default: ''
21+
api-delay:
22+
description: 'Delay in seconds between API calls to avoid rate limits (0 = no delay)'
23+
required: false
24+
default: '0'
2125

2226
outputs:
2327
report-content:
@@ -35,4 +39,5 @@ runs:
3539
INPUT_GITHUB_TOKEN: ${{ inputs.github-token }}
3640
INPUT_ORGANIZATION: ${{ inputs.organization }}
3741
INPUT_OUTPUT_FORMAT: ${{ inputs.output-format }}
38-
INPUT_EXCLUDE_REPOS: ${{ inputs.exclude-repos }}
42+
INPUT_EXCLUDE_REPOS: ${{ inputs.exclude-repos }}
43+
INPUT_API_DELAY: ${{ inputs.api-delay }}

.github/actions/weekly-report/generate-report.sh

Lines changed: 196 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#!/bin/bash
22
set -e
33

4+
echo "DEBUG: Starting weekly report generation"
5+
echo "DEBUG: Environment check - GITHUB_OUTPUT: ${GITHUB_OUTPUT:-NOT_SET}"
6+
47
# Get inputs
58
GITHUB_TOKEN="${INPUT_GITHUB_TOKEN}"
69
ORGANIZATION="${INPUT_ORGANIZATION:-QuantEcon}"
710
OUTPUT_FORMAT="${INPUT_OUTPUT_FORMAT:-markdown}"
811
EXCLUDE_REPOS="${INPUT_EXCLUDE_REPOS:-}"
12+
API_DELAY="${INPUT_API_DELAY:-0}" # Optional delay between API calls in seconds
13+
14+
echo "DEBUG: Inputs - ORG: $ORGANIZATION, FORMAT: $OUTPUT_FORMAT, EXCLUDE: $EXCLUDE_REPOS"
915

1016
# Date calculations for last week
1117
WEEK_AGO=$(date -d "7 days ago" -u +"%Y-%m-%dT%H:%M:%SZ")
@@ -14,13 +20,72 @@ NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
1420
echo "Generating weekly report for ${ORGANIZATION} organization"
1521
echo "Period: ${WEEK_AGO} to ${NOW}"
1622

17-
# Function to make GitHub API calls
23+
# Function to make GitHub API calls with rate limit handling
1824
api_call() {
1925
local endpoint="$1"
2026
local page="${2:-1}"
21-
curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
22-
-H "Accept: application/vnd.github.v3+json" \
23-
"https://api.github.com${endpoint}?page=${page}&per_page=100"
27+
local max_retries=3
28+
local retry_count=0
29+
local delay="${API_DELAY:-0}"
30+
31+
# Add delay between requests if specified
32+
if [ "$delay" -gt 0 ]; then
33+
sleep "$delay"
34+
fi
35+
36+
while [ $retry_count -lt $max_retries ]; do
37+
# Construct URL with proper query parameter handling
38+
local url="https://api.github.com${endpoint}"
39+
if [[ "$endpoint" == *"?"* ]]; then
40+
url="${url}&page=${page}&per_page=100"
41+
else
42+
url="${url}?page=${page}&per_page=100"
43+
fi
44+
45+
local response=$(curl -s -w "\n%{http_code}" -H "Authorization: token ${GITHUB_TOKEN}" \
46+
-H "Accept: application/vnd.github.v3+json" \
47+
"$url")
48+
49+
local http_code=$(echo "$response" | tail -n1)
50+
local body=$(echo "$response" | head -n -1)
51+
52+
case "$http_code" in
53+
200)
54+
echo "$body"
55+
return 0
56+
;;
57+
403)
58+
# Check if it's a rate limit error
59+
if echo "$body" | jq -e '.message' 2>/dev/null | grep -q "rate limit"; then
60+
retry_count=$((retry_count + 1))
61+
if [ $retry_count -lt $max_retries ]; then
62+
local wait_time=$((retry_count * retry_count * 60)) # Exponential backoff: 1min, 4min, 9min
63+
echo "Rate limit exceeded for $endpoint. Waiting ${wait_time}s before retry $retry_count/$max_retries..." >&2
64+
sleep "$wait_time"
65+
continue
66+
else
67+
echo "Rate limit exceeded for $endpoint after $max_retries retries. Data will be incomplete." >&2
68+
echo '{"error": "rate_limit_exceeded", "message": "API rate limit exceeded"}'
69+
return 1
70+
fi
71+
else
72+
echo "Access forbidden for $endpoint: $body" >&2
73+
echo '{"error": "forbidden", "message": "Access forbidden"}'
74+
return 1
75+
fi
76+
;;
77+
404)
78+
echo "Repository not found: $endpoint" >&2
79+
echo '{"error": "not_found", "message": "Repository not found"}'
80+
return 1
81+
;;
82+
*)
83+
echo "API call failed for $endpoint with status $http_code: $body" >&2
84+
echo '{"error": "api_error", "message": "API call failed"}'
85+
return 1
86+
;;
87+
esac
88+
done
2489
}
2590

2691
# Get repositories with recent activity using GitHub Search API
@@ -83,80 +148,179 @@ if [ -n "$EXCLUDE_REPOS" ]; then
83148
fi
84149

85150
# Initialize report variables
151+
total_current_issues=0
86152
total_opened_issues=0
87153
total_closed_issues=0
88154
total_merged_prs=0
155+
failed_repos=0
156+
rate_limited_repos=0
89157
report_content=""
90158

91159
# Start building the report
92160
if [ "$OUTPUT_FORMAT" = "markdown" ]; then
93-
report_content="# QuantEcon Weekly Report\n\n"
94-
report_content+="**Report Period:** $(date -d "$WEEK_AGO" '+%B %d, %Y') - $(date -d "$NOW" '+%B %d, %Y')\n\n"
95-
report_content+="## Summary\n\n"
96-
report_content+="| Repository | Opened Issues | Closed Issues | Merged PRs |\n"
97-
report_content+="|------------|---------------|---------------|------------|\n"
161+
report_content="# QuantEcon Weekly Report
162+
163+
**Report Period:** $(date -d "$WEEK_AGO" '+%B %d, %Y') - $(date -d "$NOW" '+%B %d, %Y')
164+
165+
## Summary
166+
167+
| Repository | Total Current Issues | Opened Issues | Closed Issues | Merged PRs |
168+
|------------|---------------------|---------------|---------------|------------|"
169+
echo "DEBUG: Initial report content set, length: ${#report_content}"
98170
fi
99171

100172
# Process each repository
173+
repo_count=0
101174
while IFS= read -r repo; do
102175
[ -z "$repo" ] && continue
176+
repo_count=$((repo_count + 1))
103177

104178
echo "Processing repository: $repo"
105179

180+
# Count total current open issues
181+
current_issues_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=open")
182+
if [ $? -eq 0 ]; then
183+
current_issues=$(echo "$current_issues_response" | jq 'if type == "array" then [.[] | select(.pull_request == null)] | length else 0 end')
184+
else
185+
current_issues=0
186+
if echo "$current_issues_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then
187+
rate_limited_repos=$((rate_limited_repos + 1))
188+
else
189+
failed_repos=$((failed_repos + 1))
190+
fi
191+
fi
192+
106193
# Count opened issues in the last week
107-
opened_issues=$(api_call "/repos/${ORGANIZATION}/${repo}/issues" | \
108-
jq --arg since "$WEEK_AGO" '[.[] | select(.created_at >= $since and .pull_request == null)] | length')
194+
opened_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues")
195+
if [ $? -eq 0 ]; then
196+
opened_issues=$(echo "$opened_response" | jq --arg since "$WEEK_AGO" 'if type == "array" then [.[] | select(.created_at >= $since and .pull_request == null)] | length else 0 end')
197+
else
198+
opened_issues=0
199+
if echo "$opened_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then
200+
rate_limited_repos=$((rate_limited_repos + 1))
201+
else
202+
failed_repos=$((failed_repos + 1))
203+
fi
204+
fi
109205

110206
# Count closed issues in the last week
111-
closed_issues=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=closed" | \
112-
jq --arg since "$WEEK_AGO" '[.[] | select(.closed_at >= $since and .pull_request == null)] | length')
207+
closed_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=closed")
208+
if [ $? -eq 0 ]; then
209+
closed_issues=$(echo "$closed_response" | jq --arg since "$WEEK_AGO" 'if type == "array" then [.[] | select(.closed_at != null and .closed_at >= $since and .pull_request == null)] | length else 0 end')
210+
else
211+
closed_issues=0
212+
if echo "$closed_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then
213+
rate_limited_repos=$((rate_limited_repos + 1))
214+
else
215+
failed_repos=$((failed_repos + 1))
216+
fi
217+
fi
113218

114219
# Count merged PRs in the last week
115-
merged_prs=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=closed" | \
116-
jq --arg since "$WEEK_AGO" '[.[] | select(.merged_at != null and .merged_at >= $since)] | length')
220+
prs_response=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=closed")
221+
if [ $? -eq 0 ]; then
222+
merged_prs=$(echo "$prs_response" | jq --arg since "$WEEK_AGO" 'if type == "array" then [.[] | select(.merged_at != null and .merged_at >= $since)] | length else 0 end')
223+
else
224+
merged_prs=0
225+
if echo "$prs_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then
226+
rate_limited_repos=$((rate_limited_repos + 1))
227+
else
228+
failed_repos=$((failed_repos + 1))
229+
fi
230+
fi
117231

118232
# Handle null/empty values
233+
current_issues=${current_issues:-0}
119234
opened_issues=${opened_issues:-0}
120235
closed_issues=${closed_issues:-0}
121236
merged_prs=${merged_prs:-0}
122237

123238
# Add to totals
239+
total_current_issues=$((total_current_issues + current_issues))
124240
total_opened_issues=$((total_opened_issues + opened_issues))
125241
total_closed_issues=$((total_closed_issues + closed_issues))
126242
total_merged_prs=$((total_merged_prs + merged_prs))
127243

128-
# Add to report if there's activity
129-
if [ $((opened_issues + closed_issues + merged_prs)) -gt 0 ]; then
244+
# Add to report if there's activity or current open issues
245+
if [ $((current_issues + opened_issues + closed_issues + merged_prs)) -gt 0 ]; then
130246
if [ "$OUTPUT_FORMAT" = "markdown" ]; then
131-
report_content+="| $repo | $opened_issues | $closed_issues | $merged_prs |\n"
247+
report_content="${report_content}
248+
| $repo | $current_issues | $opened_issues | $closed_issues | $merged_prs |"
132249
fi
133250
fi
134251

135252
done <<< "$repo_names"
136253

254+
echo "DEBUG: Processed $repo_count repositories"
255+
echo "DEBUG: Final report content length: ${#report_content}"
256+
137257
# Add summary to report
138258
if [ "$OUTPUT_FORMAT" = "markdown" ]; then
139-
report_content+="|**Total**|**$total_opened_issues**|**$total_closed_issues**|**$total_merged_prs**|\n\n"
140-
report_content+="## Details\n\n"
141-
report_content+="- **Total Repositories Checked:** $(echo "$repo_names" | wc -l)\n"
142-
report_content+="- **Total Issues Opened:** $total_opened_issues\n"
143-
report_content+="- **Total Issues Closed:** $total_closed_issues\n"
144-
report_content+="- **Total PRs Merged:** $total_merged_prs\n\n"
145-
report_content+="*Report generated on $(date) by QuantEcon Weekly Report Action*\n"
259+
report_content="${report_content}
260+
|**Total**|**$total_current_issues**|**$total_opened_issues**|**$total_closed_issues**|**$total_merged_prs**|
261+
262+
## Details
263+
264+
- **Total Repositories Checked:** $(echo "$repo_names" | wc -l)
265+
- **Total Current Open Issues:** $total_current_issues
266+
- **Total Issues Opened:** $total_opened_issues
267+
- **Total Issues Closed:** $total_closed_issues
268+
- **Total PRs Merged:** $total_merged_prs"
269+
270+
# Add warnings about incomplete data if any API calls failed
271+
if [ $rate_limited_repos -gt 0 ] || [ $failed_repos -gt 0 ]; then
272+
report_content="${report_content}
273+
274+
### ⚠️ Data Completeness Warnings
275+
"
276+
if [ $rate_limited_repos -gt 0 ]; then
277+
report_content="${report_content}
278+
- **Rate Limited:** $rate_limited_repos API calls hit rate limits. Data may be incomplete."
279+
fi
280+
if [ $failed_repos -gt 0 ]; then
281+
report_content="${report_content}
282+
- **Failed Requests:** $failed_repos API calls failed. Data may be incomplete."
283+
fi
284+
report_content="${report_content}
285+
286+
*Consider adding API delays or running during off-peak hours to avoid rate limits.*"
287+
fi
288+
289+
report_content="${report_content}
290+
291+
*Report generated on $(date) by QuantEcon Weekly Report Action*"
146292
fi
147293

148294
# Create summary
149-
summary="Week Summary: $total_opened_issues issues opened, $total_closed_issues issues closed, $total_merged_prs PRs merged"
295+
summary="Week Summary: $total_current_issues current open issues, $total_opened_issues issues opened, $total_closed_issues issues closed, $total_merged_prs PRs merged"
150296

151297
# Save report to file
152-
echo -e "$report_content" > weekly-report.md
298+
echo "$report_content" > weekly-report.md
153299

154-
# Set outputs
155-
echo "report-content<<EOF" >> $GITHUB_OUTPUT
156-
echo -e "$report_content" >> $GITHUB_OUTPUT
157-
echo "EOF" >> $GITHUB_OUTPUT
300+
# Debug: Check if GITHUB_OUTPUT is set and accessible
301+
echo "DEBUG: GITHUB_OUTPUT environment variable: ${GITHUB_OUTPUT:-NOT_SET}"
302+
echo "DEBUG: Report content length: ${#report_content}"
303+
echo "DEBUG: Summary: $summary"
158304

159-
echo "report-summary=$summary" >> $GITHUB_OUTPUT
305+
# Set outputs
306+
if [ -n "$GITHUB_OUTPUT" ]; then
307+
echo "DEBUG: Writing to GITHUB_OUTPUT file"
308+
echo "DEBUG: Content preview (first 100 chars): ${report_content:0:100}"
309+
echo "DEBUG: Summary preview: $summary"
310+
311+
# Use a unique delimiter to avoid conflicts with content
312+
delimiter="QUANTECON_REPORT_END_$(date +%s)"
313+
echo "report-content<<${delimiter}" >> "$GITHUB_OUTPUT"
314+
echo "$report_content" >> "$GITHUB_OUTPUT"
315+
echo "${delimiter}" >> "$GITHUB_OUTPUT"
316+
317+
echo "report-summary=$summary" >> "$GITHUB_OUTPUT"
318+
319+
echo "DEBUG: Outputs written to GITHUB_OUTPUT"
320+
echo "DEBUG: GITHUB_OUTPUT file size: $(wc -c < "$GITHUB_OUTPUT")"
321+
else
322+
echo "ERROR: GITHUB_OUTPUT environment variable not set!"
323+
fi
160324

161325
echo "Weekly report generated successfully!"
162326
echo "Summary: $summary"

.github/workflows/weekly-report.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,17 @@ jobs:
3333
uses: actions/github-script@v7
3434
with:
3535
script: |
36-
const reportContent = `${{ steps.report.outputs.report-content }}`;
37-
const summary = `${{ steps.report.outputs.report-summary }}`;
36+
const fs = require('fs');
37+
38+
// Read the report content directly from the generated file
39+
let reportContent = '';
40+
try {
41+
reportContent = fs.readFileSync('weekly-report.md', 'utf8');
42+
console.log(`Successfully read report file, length: ${reportContent.length}`);
43+
} catch (error) {
44+
console.error('Failed to read weekly-report.md:', error);
45+
reportContent = 'Error: Unable to load weekly report content.';
46+
}
3847
3948
// Create issue title with current date
4049
const now = new Date();
@@ -55,7 +64,7 @@ jobs:
5564
});
5665
5766
console.log(`Created issue: ${title}`);
58-
console.log(`Summary: ${summary}`);
67+
console.log(`Report content length: ${reportContent.length}`);
5968
6069
- name: Upload report as artifact
6170
uses: actions/upload-artifact@v4

0 commit comments

Comments
 (0)