@@ -59,25 +59,71 @@ def __init__(self, logger: logging.Logger, github_token: str, github_repo: str,
5959 self .logger = logger
6060 self .github_api_url = "https://api.github.com"
6161
62- def _make_github_request (self , method : str , endpoint : str , json : Optional [dict ] = None ) -> requests .Response :
62+ def _make_github_request (self , method : str , endpoint : str , json : Optional [dict ] = None , params : Optional [ dict ] = None ) -> requests .Response :
6363 """
64- Makes a request to the GitHub API.
64+ Makes a request to the GitHub API with retry logic for transient errors and rate limiting .
6565
6666 Args:
6767 method (str): The HTTP method to use (e.g., "GET", "POST", "PATCH").
6868 endpoint (str): The API endpoint to call.
6969 json (Optional[dict]): The JSON payload to send with the request.
70+ params (Optional[dict]): The URL parameters to send with the request.
7071
7172 Returns:
7273 requests.Response: The response from the API.
7374 """
75+ import time
7476 url = f"{ self .github_api_url } /{ endpoint } "
75- response = requests .request (method , url , headers = self .headers , json = json )
77+ max_retries = 5
78+ backoff = 2
7679
77- if not response .ok :
78- self .logger .error (f"Failed GitHub API request to { endpoint } : { response .status_code } - { response .text } " )
79- response .raise_for_status ()
80-
80+ for attempt in range (max_retries ):
81+ try :
82+ response = requests .request (method , url , headers = self .headers , json = json , params = params )
83+
84+ # Check for rate limiting / secondary rate limit (403, 429)
85+ if response .status_code in [403 , 429 ]:
86+ retry_after = response .headers .get ("Retry-After" )
87+ if retry_after :
88+ sleep_seconds = int (retry_after )
89+ else :
90+ sleep_seconds = backoff
91+
92+ self .logger .warning (
93+ f"GitHub API rate limit hit ({ response .status_code } ) on { endpoint } . "
94+ f"Retrying in { sleep_seconds } seconds... (Attempt { attempt + 1 } /{ max_retries } )"
95+ )
96+ time .sleep (sleep_seconds )
97+ backoff *= 2
98+ continue
99+
100+ # Check for transient server errors (500, 502, 503, 504)
101+ if response .status_code in [500 , 502 , 503 , 504 ]:
102+ self .logger .warning (
103+ f"GitHub API server error ({ response .status_code } ) on { endpoint } . "
104+ f"Retrying in { backoff } seconds... (Attempt { attempt + 1 } /{ max_retries } )"
105+ )
106+ time .sleep (backoff )
107+ backoff *= 2
108+ continue
109+
110+ if not response .ok :
111+ self .logger .error (f"Failed GitHub API request to { endpoint } : { response .status_code } - { response .text } " )
112+ response .raise_for_status ()
113+
114+ return response
115+ except requests .exceptions .RequestException as e :
116+ if attempt == max_retries - 1 :
117+ raise
118+ self .logger .warning (
119+ f"GitHub API request exception on { endpoint } : { e } . "
120+ f"Retrying in { backoff } seconds... (Attempt { attempt + 1 } /{ max_retries } )"
121+ )
122+ time .sleep (backoff )
123+ backoff *= 2
124+
125+ self .logger .error (f"Failed GitHub API request to { endpoint } after { max_retries } attempts." )
126+ response .raise_for_status ()
81127 return response
82128
83129 def _send_email (self , title : str , body : str , recipient : str ) -> None :
@@ -97,13 +143,13 @@ def _send_email(self, title: str, body: str, recipient: str) -> None:
97143
98144 def _get_open_issues (self , title : str ) -> List [GitHubIssue ]:
99145 """
100- Retrieves the number of open GitHub issues with a given title.
146+ Retrieves the open GitHub issues with a given title.
101147
102148 Args:
103149 title (str): The title of the GitHub issue.
104150 """
105- endpoint = f"search/issues?q= is:issue+ repo:{ self .github_repo } + in:title+ { title } + is:open"
106- response = self ._make_github_request ("GET" , endpoint )
151+ q = f' is:issue repo:{ self .github_repo } in:title " { title } " is:open'
152+ response = self ._make_github_request ("GET" , "search/issues" , params = { "q" : q } )
107153 issues = response .json ().get ('items' , [])
108154 parsed_issues = []
109155 for issue in issues :
0 commit comments