26
26
import subprocess
27
27
from requests .adapters import HTTPAdapter
28
28
from requests .packages .urllib3 .util .retry import Retry
29
- # from absl import logging # Removed
30
-
31
29
# Constants for GitHub API interaction
32
30
RETRIES = 3
33
31
BACKOFF = 5
34
32
RETRY_STATUS = (403 , 500 , 502 , 504 ) # HTTP status codes to retry on
35
33
TIMEOUT = 5 # Default timeout for requests in seconds
36
34
TIMEOUT_LONG = 20 # Longer timeout, currently not used by functions in this script
37
35
38
- # Global variables for the target repository.
39
- # These are populated by set_repo_url_standalone() after determining
40
- # the owner and repo from arguments or git configuration.
36
+ # Global variables for the target repository, populated by set_repo_url_standalone()
41
37
OWNER = ''
42
38
REPO = ''
43
- BASE_URL = 'https://api.github.com' # Base URL for GitHub API
44
- GITHUB_API_URL = '' # Dynamically constructed API URL for the specific repository
45
-
46
- # logging.set_verbosity(logging.WARNING) # Removed
39
+ BASE_URL = 'https://api.github.com'
40
+ GITHUB_API_URL = ''
47
41
48
42
49
43
def set_repo_url_standalone (owner_name , repo_name ):
@@ -80,7 +74,7 @@ def get_pull_request_review_comments(token, pull_number, since=None):
80
74
81
75
base_params = {'per_page' : per_page }
82
76
if since :
83
- base_params ['since' ] = since # Filter comments by timestamp
77
+ base_params ['since' ] = since
84
78
85
79
while True :
86
80
current_page_params = base_params .copy ()
@@ -90,22 +84,21 @@ def get_pull_request_review_comments(token, pull_number, since=None):
90
84
with requests_retry_session ().get (url , headers = headers , params = current_page_params ,
91
85
stream = True , timeout = TIMEOUT ) as response :
92
86
response .raise_for_status ()
93
- # logging.info("get_pull_request_review_comments: %s params %s response: %s", url, current_page_params, response) # Removed
94
87
95
88
current_page_results = response .json ()
96
- if not current_page_results : # No more data on this page
89
+ if not current_page_results : # No more data
97
90
break
98
91
99
92
results .extend (current_page_results )
100
93
101
- if len (current_page_results ) < per_page : # Last page
94
+ if len (current_page_results ) < per_page : # Reached last page
102
95
break
103
96
104
97
page += 1
105
98
106
99
except requests .exceptions .RequestException as e :
107
100
sys .stderr .write (f"Error: Failed to fetch review comments (page { page } , params: { current_page_params } ) for PR { pull_number } : { e } \n " )
108
- return None # Indicate error
101
+ return None
109
102
return results
110
103
111
104
@@ -127,7 +120,6 @@ def list_pull_requests(token, state, head, base):
127
120
try :
128
121
with requests_retry_session ().get (url , headers = headers , params = params ,
129
122
stream = True , timeout = TIMEOUT ) as response :
130
- # logging.info("list_pull_requests: %s params: %s response: %s", url, params, response) # Removed
131
123
response .raise_for_status ()
132
124
current_page_results = response .json ()
133
125
if not current_page_results :
@@ -136,7 +128,7 @@ def list_pull_requests(token, state, head, base):
136
128
keep_going = (len (current_page_results ) == per_page )
137
129
except requests .exceptions .RequestException as e :
138
130
sys .stderr .write (f"Error: Failed to list pull requests (page { params .get ('page' , 'N/A' )} , params: { params } ) for { OWNER } /{ REPO } : { e } \n " )
139
- return None # Indicate error
131
+ return None
140
132
return results
141
133
142
134
@@ -157,7 +149,6 @@ def get_pull_request_reviews(token, owner, repo, pull_number):
157
149
try :
158
150
with requests_retry_session ().get (url , headers = headers , params = params ,
159
151
stream = True , timeout = TIMEOUT ) as response :
160
- # logging.info("get_pull_request_reviews: %s params: %s response: %s", url, params, response) # Removed
161
152
response .raise_for_status ()
162
153
current_page_results = response .json ()
163
154
if not current_page_results :
@@ -166,7 +157,7 @@ def get_pull_request_reviews(token, owner, repo, pull_number):
166
157
keep_going = (len (current_page_results ) == per_page )
167
158
except requests .exceptions .RequestException as e :
168
159
sys .stderr .write (f"Error: Failed to list pull request reviews (page { params .get ('page' , 'N/A' )} , params: { params } ) for PR { pull_number } in { owner } /{ repo } : { e } \n " )
169
- return None # Indicate error
160
+ return None
170
161
return results
171
162
172
163
@@ -220,12 +211,11 @@ def main():
220
211
sys .stderr .write (f"Determined repository: { determined_owner } /{ determined_repo } from git remote.\n " )
221
212
except (subprocess .CalledProcessError , FileNotFoundError , UnicodeDecodeError ) as e :
222
213
sys .stderr .write (f"Could not automatically determine repository from git remote: { e } \n " )
223
- except Exception as e : # Catch any other unexpected error during git processing, though less likely .
214
+ except Exception as e : # Catch any other unexpected error.
224
215
sys .stderr .write (f"An unexpected error occurred while determining repository: { e } \n " )
225
216
226
217
def parse_repo_url (url_string ):
227
218
"""Parses owner and repository name from various GitHub URL formats."""
228
- # Handles https://github.com/owner/repo.git, [email protected] :owner/repo.git, and URLs without .git suffix.
229
219
url_match = re .search (r"(?:(?:https?://github\.com/)|(?:git@github\.com:))([^/]+)/([^/.]+?)(?:\.git)?/?$" , url_string )
230
220
if url_match :
231
221
return url_match .group (1 ), url_match .group (2 )
@@ -326,30 +316,27 @@ def parse_repo_url(url_string):
326
316
final_owner = args .owner
327
317
final_repo = args .repo
328
318
sys .stderr .write (f"Using repository from --owner/--repo args: { final_owner } /{ final_repo } \n " )
329
- else : # User set one but not the other (and the other wasn't available from a default)
319
+ else :
330
320
sys .stderr .write (f"Error: Both --owner and --repo must be specified if one is provided explicitly (and --url is not used).{ error_suffix } \n " )
331
321
sys .exit (1 )
332
322
elif args .owner and args .repo : # Both args have values, from successful auto-detection
333
323
final_owner = args .owner
334
324
final_repo = args .repo
335
- elif args .owner or args .repo : # Only one has a value (e.g. auto-detect only found one )
325
+ elif args .owner or args .repo : # Only one has a value from auto-detection (e.g. git remote parsing failed partially )
336
326
sys .stderr .write (f"Error: Both --owner and --repo are required if not using --url, and auto-detection was incomplete.{ error_suffix } \n " )
337
327
sys .exit (1 )
338
- # If final_owner/repo are still None here, it means auto-detection failed and user provided nothing.
328
+ # If final_owner/repo are still None here, it means auto-detection failed AND user provided nothing.
339
329
340
330
if not final_owner or not final_repo :
341
331
sys .stderr .write (f"Error: Could not determine repository. Please specify --url, OR both --owner and --repo, OR ensure git remote 'origin' is configured correctly.{ error_suffix } \n " )
342
332
sys .exit (1 )
343
333
344
- # Set global repository variables for API calls
345
334
if not set_repo_url_standalone (final_owner , final_repo ):
346
- # This path should ideally not be reached if previous checks are robust.
347
335
sys .stderr .write (f"Error: Could not set repository to { final_owner } /{ final_repo } . Ensure owner/repo are correct.{ error_suffix } \n " )
348
336
sys .exit (1 )
349
337
350
- # Determine Pull Request number
351
338
pull_request_number = args .pull_number
352
- current_branch_for_pr_check = None
339
+ current_branch_for_pr_check = None # Store branch name if auto-detecting PR
353
340
if not pull_request_number :
354
341
sys .stderr .write ("Pull number not specified, attempting to find PR for current branch...\n " )
355
342
current_branch_for_pr_check = get_current_branch_name ()
@@ -359,22 +346,19 @@ def parse_repo_url(url_string):
359
346
if pull_request_number :
360
347
sys .stderr .write (f"Found PR #{ pull_request_number } for branch { current_branch_for_pr_check } .\n " )
361
348
else :
362
- # Informational, actual error handled by `if not pull_request_number:` below
363
- sys .stderr .write (f"No open PR found for branch { current_branch_for_pr_check } in { OWNER } /{ REPO } .\n " )
349
+ sys .stderr .write (f"No open PR found for branch { current_branch_for_pr_check } in { OWNER } /{ REPO } .\n " ) # Informational
364
350
else :
365
351
sys .stderr .write (f"Error: Could not determine current git branch. Cannot find PR automatically.{ error_suffix } \n " )
366
352
sys .exit (1 )
367
353
368
- if not pull_request_number :
354
+ if not pull_request_number : # Final check for PR number
369
355
error_message = "Error: Pull request number is required."
370
- if not args .pull_number and current_branch_for_pr_check :
356
+ if not args .pull_number and current_branch_for_pr_check : # Auto-detect branch ok, but no PR found
371
357
error_message = f"Error: Pull request number not specified and no open PR found for branch { current_branch_for_pr_check } ."
372
- elif not args .pull_number and not current_branch_for_pr_check : # Should have been caught and exited above.
373
- error_message = "Error: Pull request number not specified and could not determine current git branch."
358
+ # The case where current_branch_for_pr_check is None (git branch fail) is caught and exited above.
374
359
sys .stderr .write (f"{ error_message } { error_suffix } \n " )
375
360
sys .exit (1 )
376
361
377
- # Fetch overall reviews first
378
362
sys .stderr .write (f"Fetching overall reviews for PR #{ pull_request_number } from { OWNER } /{ REPO } ...\n " )
379
363
overall_reviews = get_pull_request_reviews (args .token , OWNER , REPO , pull_request_number )
380
364
@@ -490,47 +474,24 @@ def parse_repo_url(url_string):
490
474
# Note: The decision to exit if only line comments fail vs. if only overall reviews fail could be nuanced.
491
475
# For now, failure to fetch either is treated as a critical error for the script's purpose.
492
476
493
- # Handling for empty line comments will be just before their processing loop.
494
- # if not comments: (handled later)
495
-
496
- # Initialize timestamps for 'next command' suggestion
497
- latest_overall_review_activity_dt = None
498
- latest_line_comment_activity_dt = None
499
- processed_comments_count = 0
500
-
501
- # Only print line comments header if there are comments to process
502
- # The 'comments' list here has already been checked for None (API error)
503
- # and for being empty (no comments found, in which case script would have exited).
504
- # However, all comments could be filtered out by status or content.
505
- # So, we'll print the header, and if nothing follows, it's acceptable.
506
- # A more robust check would be to see if any comment *will* be printed.
507
- # For now, let's check if the list is non-empty before printing the header.
508
- # The user's request was "if there are no review comments to display".
509
- # This means after all filtering. The current loop structure processes then prints.
510
- # A simple way is to print header only if `comments` list is not empty,
511
- # and then if the loop results in `processed_comments_count == 0`, the section will be empty.
512
- # Or, delay printing header until first comment is processed.
513
-
514
- # Let's try: print header only if comments list is not empty *before* the loop.
515
- # If all get filtered out, an empty section is fine.
516
- # The existing "No review comments found..." handles the case of an initially empty list.
517
- # The current plan asks for "processed_comments_count > 0". This requires a look-ahead or restructuring.
518
-
519
- # Simpler approach: If the `comments` list (from API) is not empty, print header.
520
- # If all get filtered out inside the loop, the section will be empty.
521
- # The earlier check `elif not comments:` handles the case of truly no comments from API.
522
- # So, if we reach here, `comments` is a non-empty list.
523
- # The condition should be: if any comments *survive* the loop's internal filters.
524
- # This is best done by checking `processed_comments_count` *after* the loop,
525
- # but the header needs to be printed *before*.
526
- # So, we print the header if `comments` is not empty, and accept an empty section if all are filtered.
527
- # The user's request can be interpreted as "don't print the header if the `comments` list is empty
528
- # *after fetching and initial checks*".
529
-
530
- if comments : # If the list from API (after None check) is not empty
477
+ # Initialize tracking variables early - MOVED TO TOP OF MAIN
478
+ # latest_overall_review_activity_dt = None
479
+ # latest_line_comment_activity_dt = None
480
+ # processed_comments_count = 0 # This is specifically for line comments
481
+
482
+ # Handling for line comments
483
+ if not comments : # comments is an empty list here (None case handled above)
484
+ sys .stderr .write (f"No line comments found for PR #{ pull_request_number } (or matching filters).\n " )
485
+ # If there were also no overall reviews, and no line comments, then nothing to show.
486
+ # The 'next command' suggestion logic below will still run if overall_reviews had content.
487
+ if not filtered_overall_reviews : # and not comments (implicitly true here)
488
+ # Only return (and skip 'next command' suggestion) if NO content at all was printed.
489
+ # If overall_reviews were printed, we still want the 'next command' suggestion.
490
+ pass # Let it fall through to the 'next command' suggestion logic
491
+ else :
531
492
print ("# Review Comments\n \n " )
532
493
533
- for comment in comments : # ` comments` is guaranteed to be a list here
494
+ for comment in comments : # if comments is empty, this loop is skipped.
534
495
created_at_str = comment .get ("created_at" )
535
496
536
497
current_pos = comment .get ("position" )
0 commit comments