@@ -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
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
89
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/${repo_owner} /${repo_name} /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.
@@ -106,7 +181,11 @@ remove_label() {
106
181
local pr_number=" $1 "
107
182
local label=" $2 "
108
183
109
- github_api " DELETE" " /repos/${repo_owner} /${repo_name} /issues/${pr_number} /labels/${label} " || true
184
+ # URL encode the label name by replacing spaces with %20:
185
+ local encoded_label=" ${label// /% 20} "
186
+
187
+ debug_log " Removing label '${label} ' from PR #${pr_number} "
188
+ github_api " DELETE" " /repos/${repo_owner} /${repo_name} /issues/${pr_number} /labels/${encoded_label} "
110
189
}
111
190
112
191
# Main execution sequence.
@@ -119,13 +198,19 @@ main() {
119
198
120
199
while true ; do
121
200
# Fetch current page of PRs:
122
- page_data=$( github_api " GET" " /repos/${repo_owner} /${repo_name} /pulls?state=open&per_page=100&page=${page} " )
201
+ debug_log " Fetching page $page of PRs"
202
+ if ! page_data=$( github_api " GET" " /repos/${repo_owner} /${repo_name} /pulls?state=open&per_page=100&page=${page} " ) ; then
203
+ echo " Error fetching PRs on page $page , aborting" >&2
204
+ exit 1
205
+ fi
123
206
124
207
# Check if we got any results:
125
208
page_count=$( echo " $page_data " | jq length)
209
+ debug_log " Got $page_count PRs on page $page "
126
210
127
211
if [ " $page_count " -eq 0 ]; then
128
212
# No more results, break the loop
213
+ debug_log " No more PRs, breaking pagination loop"
129
214
break
130
215
fi
131
216
@@ -137,14 +222,14 @@ main() {
137
222
done
138
223
139
224
# Check if we found any PRs:
140
- pr_count =$( echo " $open_prs " | jq length)
141
- if [ " $pr_count " -eq 0 ]; then
225
+ total_prs =$( echo " $open_prs " | jq length)
226
+ if [ " $total_prs " -eq 0 ]; then
142
227
echo " No open pull requests found."
143
228
print_success
144
229
exit 0
145
230
fi
146
231
147
- echo " Found ${pr_count } open pull requests."
232
+ echo " Found ${total_prs } open pull requests."
148
233
149
234
# Create arrays to store mappings and track labeled PRs:
150
235
declare -a issue_prs_keys
@@ -158,65 +243,93 @@ main() {
158
243
159
244
if ! echo " $labeled_prs_data " | jq -e ' if type=="array" then true else false end' > /dev/null 2>&1 ; then
160
245
echo " Warning: Invalid response when fetching labeled PRs: ${labeled_prs_data} " >&2
246
+ debug_log " Full labeled PRs response: $labeled_prs_data "
161
247
elif [ -n " $labeled_prs_data " ]; then
162
248
while IFS= read -r labeled_pr; do
163
249
pr_number=$( echo " $labeled_pr " | jq -r ' .number' )
164
250
labeled_prs_list+=(" $pr_number " )
251
+ debug_log " Found PR #$pr_number with duplicate label"
165
252
done < <( echo " $labeled_prs_data " | jq -c ' .[]' )
166
253
fi
167
254
echo " Found ${# labeled_prs_list[@]} PRs with duplicate label"
168
255
169
256
# Process each PR to build issue mappings:
170
257
echo " Processing PRs for issue references..."
171
- pr_count=0
172
- while IFS= read -r pr; do
258
+ debug_log " Starting to process $total_prs PRs for issue references"
259
+
260
+ # Process PRs one by one...
261
+ processed_count=0
262
+
263
+ for (( i= 0 ; i< total_prs; i++ )) ; do
264
+ pr=$( echo " $open_prs " | jq -c " .[$i ]" )
265
+
266
+ if [ -z " $pr " ] || [ " $pr " = " null" ]; then
267
+ debug_log " Warning: Empty PR data at index $i "
268
+ continue
269
+ fi
270
+
173
271
pr_number=$( echo " $pr " | jq -r ' .number' )
174
- pr_body=$( echo " $pr " | jq -r ' .body' )
272
+ if [ -z " $pr_number " ] || [ " $pr_number " = " null" ]; then
273
+ debug_log " Warning: Could not extract PR number"
274
+ continue
275
+ fi
276
+
277
+ debug_log " Processing PR #$pr_number "
278
+ pr_body=$( echo " $pr " | jq -r ' .body // ""' )
175
279
resolved_issues=$( extract_resolved_issues " $pr_body " )
176
280
177
- pr_count =$(( pr_count + 1 ))
178
- if [ $(( pr_count % 50 )) -eq 0 ]; then
179
- echo " Processed ${pr_count } PRs..."
281
+ processed_count =$(( processed_count + 1 ))
282
+ if [ $(( processed_count % 50 )) -eq 0 ]; then
283
+ echo " Processed ${processed_count } PRs..."
180
284
fi
181
285
182
286
for issue in $resolved_issues ; do
287
+ debug_log " PR #$pr_number references issue #$issue "
183
288
# Find existing issue index
184
289
index=-1
185
- for i in " ${! issue_prs_keys[@]} " ; do
186
- if [ " ${issue_prs_keys[$i ]} " = " $issue " ]; then
187
- index=$i
290
+ for j in " ${! issue_prs_keys[@]} " ; do
291
+ if [ " ${issue_prs_keys[$j ]} " = " $issue " ]; then
292
+ index=$j
188
293
break
189
294
fi
190
295
done
191
296
if [ " $index " -eq -1 ]; then
297
+ debug_log " Creating new entry for issue #$issue with PR #$pr_number "
192
298
issue_prs_keys+=(" $issue " )
193
299
issue_prs_values+=(" $pr_number " )
194
300
else
301
+ debug_log " Adding PR #$pr_number to existing issue #$issue "
195
302
issue_prs_values[index]=" ${issue_prs_values[index]} $pr_number "
196
303
fi
197
304
done
198
- done < <( echo " ${open_prs} " | jq -c ' .[]' )
305
+ done
306
+
307
+ debug_log " Finished processing all PRs for issue references"
308
+ debug_log " Found ${# issue_prs_keys[@]} unique issues referenced in PRs"
199
309
200
310
# Process the mappings to find duplicates:
201
311
declare -a should_be_labeled_list
202
312
203
313
for i in " ${! issue_prs_keys[@]} " ; do
204
314
read -r -a prs <<< " ${issue_prs_values[$i]}"
205
315
if [ ${# prs[@]} -gt 1 ]; then
316
+ debug_log " Issue #${issue_prs_keys[$i]} has ${# prs[@]} PRs: ${prs[*]} "
206
317
for pr in " ${prs[@]} " ; do
207
318
should_be_labeled_list+=(" $pr " )
208
319
done
209
320
fi
210
321
done
211
322
212
- echo " PRs that should have label: ${should_be_labeled_list[*]} "
213
- echo " PRs that currently have label: ${labeled_prs_list[*]} "
323
+ debug_log " PRs that should have label: ${should_be_labeled_list[*]:- none } "
324
+ debug_log " PRs that currently have label: ${labeled_prs_list[*]:- none } "
214
325
215
326
for pr in " ${labeled_prs_list[@]} " ; do
216
327
echo " Checking if PR #${pr} should still have label..."
217
328
if ! printf ' %s\n' " ${should_be_labeled_list[@]} " | grep -q " ^${pr} $" ; then
218
329
echo " Removing duplicate label from PR #${pr} ..."
219
330
remove_label " $pr " " $duplicate_label "
331
+ else
332
+ debug_log " PR #${pr} should keep its label"
220
333
fi
221
334
done
222
335
@@ -227,10 +340,11 @@ main() {
227
340
github_api " POST" " /repos/${repo_owner} /${repo_name} /issues/${pr} /labels" \
228
341
" {\" labels\" :[\" ${duplicate_label} \" ]}"
229
342
else
230
- echo " PR #${pr} already has label, skipping..."
343
+ debug_log " PR #${pr} already has label, skipping..."
231
344
fi
232
345
done
233
346
347
+ debug_log " Script completed successfully"
234
348
print_success
235
349
exit 0
236
350
}
0 commit comments