@@ -42,9 +42,26 @@ repo_name="stdlib"
4242# Label to add/remove for duplicate PRs:
4343duplicate_label=" Potential Duplicate"
4444
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+
4552
4653# FUNCTIONS #
4754
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+
4865# Error handler.
4966#
5067# $1 - error status
@@ -67,6 +84,10 @@ github_api() {
6784 local method=" $1 "
6885 local endpoint=" $2 "
6986 local data=" $3 "
87+ local retry_count=0
88+ local response=" "
89+ local status_code
90+ local success=false
7091
7192 # Initialize an array to hold curl headers:
7293 local headers=()
@@ -76,26 +97,80 @@ github_api() {
7697 headers+=(" -H" " Authorization: token ${GITHUB_TOKEN} " )
7798 fi
7899
100+ debug_log " Making API request: ${method} ${endpoint} "
101+
79102 # For POST/PATCH requests, always set the Content-Type header:
80103 if [ " $method " != " GET" ]; then
81104 headers+=(" -H" " Content-Type: application/json" )
82105 fi
83106
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
89139 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
90152}
91153
92154# Extracts issue numbers resolved/closed in PRs for stdlib-js/stdlib.
93155#
94156# $1 - PR body text
95157extract_resolved_issues () {
96158 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 "
99174}
100175
101176# Removes a label from a PR.
@@ -119,13 +194,19 @@ main() {
119194
120195 while true ; do
121196 # 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
123202
124203 # Check if we got any results:
125204 page_count=$( echo " $page_data " | jq length)
205+ debug_log " Got $page_count PRs on page $page "
126206
127207 if [ " $page_count " -eq 0 ]; then
128208 # No more results, break the loop
209+ debug_log " No more PRs, breaking pagination loop"
129210 break
130211 fi
131212
@@ -158,23 +239,40 @@ main() {
158239
159240 if ! echo " $labeled_prs_data " | jq -e ' if type=="array" then true else false end' > /dev/null 2>&1 ; then
160241 echo " Warning: Invalid response when fetching labeled PRs: ${labeled_prs_data} " >&2
242+ debug_log " Full labeled PRs response: $labeled_prs_data "
161243 elif [ -n " $labeled_prs_data " ]; then
162244 while IFS= read -r labeled_pr; do
163245 pr_number=$( echo " $labeled_pr " | jq -r ' .number' )
164246 labeled_prs_list+=(" $pr_number " )
247+ debug_log " Found PR #$pr_number with duplicate label"
165248 done < <( echo " $labeled_prs_data " | jq -c ' .[]' )
166249 fi
167250 echo " Found ${# labeled_prs_list[@]} PRs with duplicate label"
168251
169252 # Process each PR to build issue mappings:
170253 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...
171257 pr_count=0
258+ total_prs=$( echo " $open_prs " | jq length)
172259
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
175261 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+
176268 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 // ""' )
178276 resolved_issues=$( extract_resolved_issues " $pr_body " )
179277
180278 pr_count=$(( pr_count + 1 ))
@@ -183,6 +281,7 @@ main() {
183281 fi
184282
185283 for issue in $resolved_issues ; do
284+ debug_log " PR #$pr_number references issue #$issue "
186285 # Find existing issue index
187286 index=-1
188287 for j in " ${! issue_prs_keys[@]} " ; do
@@ -192,34 +291,42 @@ main() {
192291 fi
193292 done
194293 if [ " $index " -eq -1 ]; then
294+ debug_log " Creating new entry for issue #$issue with PR #$pr_number "
195295 issue_prs_keys+=(" $issue " )
196296 issue_prs_values+=(" $pr_number " )
197297 else
298+ debug_log " Adding PR #$pr_number to existing issue #$issue "
198299 issue_prs_values[index]=" ${issue_prs_values[index]} $pr_number "
199300 fi
200301 done
201302 done
202303
304+ debug_log " Finished processing all PRs for issue references"
305+ debug_log " Found ${# issue_prs_keys[@]} unique issues referenced in PRs"
306+
203307 # Process the mappings to find duplicates:
204308 declare -a should_be_labeled_list
205309
206310 for i in " ${! issue_prs_keys[@]} " ; do
207311 read -r -a prs <<< " ${issue_prs_values[$i]}"
208312 if [ ${# prs[@]} -gt 1 ]; then
313+ debug_log " Issue #${issue_prs_keys[$i]} has ${# prs[@]} PRs: ${prs[*]} "
209314 for pr in " ${prs[@]} " ; do
210315 should_be_labeled_list+=(" $pr " )
211316 done
212317 fi
213318 done
214319
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[*]} "
217322
218323 for pr in " ${labeled_prs_list[@]} " ; do
219324 echo " Checking if PR #${pr} should still have label..."
220325 if ! printf ' %s\n' " ${should_be_labeled_list[@]} " | grep -q " ^${pr} $" ; then
221326 echo " Removing duplicate label from PR #${pr} ..."
222327 remove_label " $pr " " $duplicate_label "
328+ else
329+ debug_log " PR #${pr} should keep its label"
223330 fi
224331 done
225332
@@ -230,10 +337,11 @@ main() {
230337 github_api " POST" " /repos/${repo_owner} /${repo_name} /issues/${pr} /labels" \
231338 " {\" labels\" :[\" ${duplicate_label} \" ]}"
232339 else
233- echo " PR #${pr} already has label, skipping..."
340+ debug_log " PR #${pr} already has label, skipping..."
234341 fi
235342 done
236343
344+ debug_log " Script completed successfully"
237345 print_success
238346 exit 0
239347}
0 commit comments