@@ -42,9 +42,26 @@ repo_name="stdlib"
42
42
# Label to add/remove for duplicate PRs:
43
43
duplicate_label=" Potential Duplicate"
44
44
45
+ # Debug mode controlled by environment variable (defaults to false if not set)
46
+ debug=" ${DEBUG:- false} "
47
+
48
+ # Configure retries for API calls
49
+ max_retries=3
50
+ retry_delay=2
51
+
45
52
46
53
# FUNCTIONS #
47
54
55
+ # Debug logging function
56
+ #
57
+ # $1 - debug message
58
+ debug_log () {
59
+ # Only print debug messages if DEBUG environment variable is set to "true"
60
+ if [ " $debug " = true ]; then
61
+ echo " [DEBUG] $1 " >&2
62
+ fi
63
+ }
64
+
48
65
# Error handler.
49
66
#
50
67
# $1 - error status
@@ -67,6 +84,10 @@ github_api() {
67
84
local method=" $1 "
68
85
local endpoint=" $2 "
69
86
local data=" $3 "
87
+ local retry_count=0
88
+ local response=" "
89
+ local status_code
90
+ local success=false
70
91
71
92
# Initialize an array to hold curl headers:
72
93
local headers=()
@@ -76,26 +97,80 @@ github_api() {
76
97
headers+=(" -H" " Authorization: token ${GITHUB_TOKEN} " )
77
98
fi
78
99
100
+ debug_log " Making API request: ${method} ${endpoint} "
101
+
79
102
# For POST/PATCH requests, always set the Content-Type header:
80
103
if [ " $method " != " GET" ]; then
81
104
headers+=(" -H" " Content-Type: application/json" )
82
105
fi
83
106
84
- # Make the API request:
85
- if [ -n " ${data} " ]; then
86
- curl -s -X " ${method} " " ${headers[@]} " -d " ${data} " " ${github_api_url}${endpoint} "
87
- else
88
- curl -s -X " ${method} " " ${headers[@]} " " ${github_api_url}${endpoint} "
107
+ # Add retry logic
108
+ while [ $retry_count -lt $max_retries ] && [ " $success " = false ]; do
109
+ if [ $retry_count -gt 0 ]; then
110
+ echo " Retrying request (attempt $(( retry_count+ 1 )) /${max_retries} )..."
111
+ sleep $retry_delay
112
+ fi
113
+
114
+ # Make the API request:
115
+ if [ -n " ${data} " ]; then
116
+ response=$( curl -s -w " %{http_code}" -X " ${method} " " ${headers[@]} " -d " ${data} " " ${github_api_url}${endpoint} " )
117
+ else
118
+ response=$( curl -s -w " %{http_code}" -X " ${method} " " ${headers[@]} " " ${github_api_url}${endpoint} " )
119
+ fi
120
+
121
+ # Extract status code (last 3 digits) and actual response (everything before)
122
+ status_code=" ${response: -3} "
123
+ response=" ${response: 0: ${# response} -3} "
124
+
125
+ debug_log " Status code: $status_code "
126
+
127
+ # Check if we got a successful response
128
+ if [[ $status_code -ge 200 && $status_code -lt 300 ]]; then
129
+ success=true
130
+ else
131
+ echo " API request failed with status $status_code : $response " >&2
132
+ retry_count=$(( retry_count+ 1 ))
133
+ fi
134
+ done
135
+
136
+ if [ " $success " = false ]; then
137
+ echo " Failed to complete API request after $max_retries attempts" >&2
138
+ return 1
89
139
fi
140
+
141
+ # Validate that response is valid JSON if expected
142
+ if ! echo " $response " | jq -e ' .' > /dev/null 2>&1 ; then
143
+ echo " Warning: Response is not valid JSON: ${response} " >&2
144
+ # Return empty JSON object as fallback
145
+ echo " {}"
146
+ return 0
147
+ fi
148
+
149
+ # Return the actual response data (without status code)
150
+ echo " $response "
151
+ return 0
90
152
}
91
153
92
154
# Extracts issue numbers resolved/closed in PRs for stdlib-js/stdlib.
93
155
#
94
156
# $1 - PR body text
95
157
extract_resolved_issues () {
96
158
local body=" $1 "
97
- echo " $body " | grep -Eio " (resolves|closes|close|fix|fixes|fixed|resolve)[[:space:]]*(#[0-9]+|https?://github\.com/stdlib-js/stdlib/issues/[0-9]+)" |
98
- grep -Eo " ([0-9]+)$" | sort -u
159
+
160
+ debug_log " Extracting resolved issues from PR body of length ${# body} chars"
161
+
162
+ # Handle empty body case
163
+ if [ -z " $body " ]; then
164
+ debug_log " PR body is empty, no issues to extract"
165
+ return 0
166
+ fi
167
+
168
+ local issues
169
+ issues=$( echo " $body " | grep -Eio " (resolves|closes|close|fix|fixes|fixed|resolve)[[:space:]]*(#[0-9]+|https?://github\.com/stdlib-js/stdlib/issues/[0-9]+)" |
170
+ grep -Eo " ([0-9]+)$" | sort -u)
171
+
172
+ debug_log " Extracted issues: $issues "
173
+ echo " $issues "
99
174
}
100
175
101
176
# Removes a label from a PR.
@@ -119,13 +194,19 @@ main() {
119
194
120
195
while true ; do
121
196
# Fetch current page of PRs:
122
- page_data=$( github_api " GET" " /repos/${repo_owner} /${repo_name} /pulls?state=open&per_page=100&page=${page} " )
197
+ debug_log " Fetching page $page of PRs"
198
+ if ! page_data=$( github_api " GET" " /repos/${repo_owner} /${repo_name} /pulls?state=open&per_page=100&page=${page} " ) ; then
199
+ echo " Error fetching PRs on page $page , aborting" >&2
200
+ exit 1
201
+ fi
123
202
124
203
# Check if we got any results:
125
204
page_count=$( echo " $page_data " | jq length)
205
+ debug_log " Got $page_count PRs on page $page "
126
206
127
207
if [ " $page_count " -eq 0 ]; then
128
208
# No more results, break the loop
209
+ debug_log " No more PRs, breaking pagination loop"
129
210
break
130
211
fi
131
212
@@ -158,23 +239,40 @@ main() {
158
239
159
240
if ! echo " $labeled_prs_data " | jq -e ' if type=="array" then true else false end' > /dev/null 2>&1 ; then
160
241
echo " Warning: Invalid response when fetching labeled PRs: ${labeled_prs_data} " >&2
242
+ debug_log " Full labeled PRs response: $labeled_prs_data "
161
243
elif [ -n " $labeled_prs_data " ]; then
162
244
while IFS= read -r labeled_pr; do
163
245
pr_number=$( echo " $labeled_pr " | jq -r ' .number' )
164
246
labeled_prs_list+=(" $pr_number " )
247
+ debug_log " Found PR #$pr_number with duplicate label"
165
248
done < <( echo " $labeled_prs_data " | jq -c ' .[]' )
166
249
fi
167
250
echo " Found ${# labeled_prs_list[@]} PRs with duplicate label"
168
251
169
252
# Process each PR to build issue mappings:
170
253
echo " Processing PRs for issue references..."
254
+ debug_log " Starting to process $pr_count PRs for issue references"
255
+
256
+ # Process PRs one by one...
171
257
pr_count=0
258
+ total_prs=$( echo " $open_prs " | jq length)
172
259
173
- total_prs=$( echo " $open_prs " | jq ' length' )
174
- for i in $( seq 0 $(( total_prs- 1 )) ) ; do
260
+ for (( i= 0 ; i< total_prs; i++ )) ; do
175
261
pr=$( echo " $open_prs " | jq -c " .[$i ]" )
262
+
263
+ if [ -z " $pr " ] || [ " $pr " = " null" ]; then
264
+ debug_log " Warning: Empty PR data at index $i "
265
+ continue
266
+ fi
267
+
176
268
pr_number=$( echo " $pr " | jq -r ' .number' )
177
- pr_body=$( echo " $pr " | jq -r ' .body' )
269
+ if [ -z " $pr_number " ] || [ " $pr_number " = " null" ]; then
270
+ debug_log " Warning: Could not extract PR number"
271
+ continue
272
+ fi
273
+
274
+ debug_log " Processing PR #$pr_number "
275
+ pr_body=$( echo " $pr " | jq -r ' .body // ""' )
178
276
resolved_issues=$( extract_resolved_issues " $pr_body " )
179
277
180
278
pr_count=$(( pr_count + 1 ))
@@ -183,6 +281,7 @@ main() {
183
281
fi
184
282
185
283
for issue in $resolved_issues ; do
284
+ debug_log " PR #$pr_number references issue #$issue "
186
285
# Find existing issue index
187
286
index=-1
188
287
for j in " ${! issue_prs_keys[@]} " ; do
@@ -192,34 +291,42 @@ main() {
192
291
fi
193
292
done
194
293
if [ " $index " -eq -1 ]; then
294
+ debug_log " Creating new entry for issue #$issue with PR #$pr_number "
195
295
issue_prs_keys+=(" $issue " )
196
296
issue_prs_values+=(" $pr_number " )
197
297
else
298
+ debug_log " Adding PR #$pr_number to existing issue #$issue "
198
299
issue_prs_values[index]=" ${issue_prs_values[index]} $pr_number "
199
300
fi
200
301
done
201
302
done
202
303
304
+ debug_log " Finished processing all PRs for issue references"
305
+ debug_log " Found ${# issue_prs_keys[@]} unique issues referenced in PRs"
306
+
203
307
# Process the mappings to find duplicates:
204
308
declare -a should_be_labeled_list
205
309
206
310
for i in " ${! issue_prs_keys[@]} " ; do
207
311
read -r -a prs <<< " ${issue_prs_values[$i]}"
208
312
if [ ${# prs[@]} -gt 1 ]; then
313
+ debug_log " Issue #${issue_prs_keys[$i]} has ${# prs[@]} PRs: ${prs[*]} "
209
314
for pr in " ${prs[@]} " ; do
210
315
should_be_labeled_list+=(" $pr " )
211
316
done
212
317
fi
213
318
done
214
319
215
- echo " PRs that should have label: ${should_be_labeled_list[*]} "
216
- echo " PRs that currently have label: ${labeled_prs_list[*]} "
320
+ debug_log " PRs that should have label: ${should_be_labeled_list[*]} "
321
+ debug_log " PRs that currently have label: ${labeled_prs_list[*]} "
217
322
218
323
for pr in " ${labeled_prs_list[@]} " ; do
219
324
echo " Checking if PR #${pr} should still have label..."
220
325
if ! printf ' %s\n' " ${should_be_labeled_list[@]} " | grep -q " ^${pr} $" ; then
221
326
echo " Removing duplicate label from PR #${pr} ..."
222
327
remove_label " $pr " " $duplicate_label "
328
+ else
329
+ debug_log " PR #${pr} should keep its label"
223
330
fi
224
331
done
225
332
@@ -230,10 +337,11 @@ main() {
230
337
github_api " POST" " /repos/${repo_owner} /${repo_name} /issues/${pr} /labels" \
231
338
" {\" labels\" :[\" ${duplicate_label} \" ]}"
232
339
else
233
- echo " PR #${pr} already has label, skipping..."
340
+ debug_log " PR #${pr} already has label, skipping..."
234
341
fi
235
342
done
236
343
344
+ debug_log " Script completed successfully"
237
345
print_success
238
346
exit 0
239
347
}
0 commit comments