Skip to content

Commit b3f3a43

Browse files
author
Your Name
committed
Enhance GitHubClient error handling: improve logging for API requests and responses, implement retry logic for repository creation, and add support for alternative permission setting endpoints.
1 parent 6bc3da6 commit b3f3a43

File tree

1 file changed

+103
-31
lines changed

1 file changed

+103
-31
lines changed

template_automation/github_client.py

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,34 @@ def _request(self, method: str, url: str, **kwargs) -> Dict[str, Any]:
109109
kwargs['verify'] = self.verify_ssl
110110

111111
# Log the request
112-
logger.debug(f"GitHub API {method} request: {url}")
112+
if 'json' in kwargs:
113+
logger.info(f"GitHub API {method} request to {url} with payload: {json.dumps(kwargs['json'])}")
114+
else:
115+
logger.info(f"GitHub API {method} request to {url}")
113116

114117
# Make the request
115-
response = self.session.request(method, url, **kwargs)
116-
117-
# Raise exception for error status codes
118-
response.raise_for_status()
119-
120-
# Return JSON data for non-empty responses
121-
if response.text:
122-
return response.json()
123-
return {}
118+
try:
119+
response = self.session.request(method, url, **kwargs)
120+
121+
# Raise exception for error status codes
122+
if response.status_code >= 400:
123+
logger.error(f"GitHub API error: {response.status_code} - {response.text}")
124+
125+
response.raise_for_status()
126+
127+
# Return JSON data for non-empty responses
128+
if response.text:
129+
return response.json()
130+
return {}
131+
except requests.exceptions.RequestException as e:
132+
if hasattr(e, 'response') and e.response is not None:
133+
try:
134+
error_body = e.response.json()
135+
logger.error(f"GitHub API error details: {json.dumps(error_body)}")
136+
except (ValueError, json.JSONDecodeError):
137+
logger.error(f"GitHub API error: {e.response.text}")
138+
logger.error(f"Request failed: {str(e)}")
139+
raise
124140

125141
def get_repository(
126142
self,
@@ -152,32 +168,61 @@ def get_repository(
152168
if e.response.status_code == 404 and create:
153169
logger.info(f"Creating repository {repo_name}")
154170

155-
# Create a new repository
171+
# Create a new repository with minimal parameters
156172
url = f"/orgs/{self.org_name}/repos"
157-
repo = self._request("POST", url, json={
158-
"name": repo_name,
159-
"private": True,
160-
"auto_init": True,
161-
"allow_squash_merge": True,
162-
"allow_merge_commit": True,
163-
"allow_rebase_merge": True,
164-
"delete_branch_on_merge": True
165-
})
173+
try:
174+
# Try with minimal parameters first
175+
repo = self._request("POST", url, json={
176+
"name": repo_name,
177+
"private": False,
178+
"auto_init": True
179+
})
180+
except requests.exceptions.HTTPError as create_error:
181+
# Log detailed error information for 422 errors
182+
if create_error.response.status_code == 422:
183+
error_response = create_error.response.json()
184+
logger.error(f"GitHub API error details: {json.dumps(error_response)}")
185+
# Try again with even more minimal parameters if it's a schema validation issue
186+
if "message" in error_response and "Validation Failed" in error_response.get("message", ""):
187+
logger.info("Retrying repository creation with minimal parameters")
188+
repo = self._request("POST", url, json={
189+
"name": repo_name,
190+
"private": True
191+
})
192+
else:
193+
raise
194+
else:
195+
raise
166196

167197
# Wait for repository initialization
168-
max_retries = 100
169-
retry_delay = 1
170-
for _ in range(max_retries):
198+
max_retries = 10
199+
retry_delay = 2
200+
for i in range(max_retries):
171201
try:
172-
self.get_branch(repo_name, "main")
202+
# Try both main and master as possible default branches
203+
for branch_name in ["main", "master"]:
204+
try:
205+
self.get_branch(repo_name, branch_name)
206+
logger.info(f"Repository initialized with default branch '{branch_name}'")
207+
break
208+
except requests.exceptions.HTTPError:
209+
pass
210+
else:
211+
# If we reach here, neither branch was found, but repo may still be usable
212+
if i == max_retries - 1:
213+
logger.warning(f"Repository {repo_name} created but default branch not found")
214+
continue
173215
break
174216
except requests.exceptions.HTTPError:
217+
logger.info(f"Waiting for repository initialization, attempt {i+1}/{max_retries}")
175218
time.sleep(retry_delay)
176-
else:
177-
raise Exception(f"Repository {repo_name} initialization timed out")
219+
retry_delay *= 1.5 # Exponential backoff
178220

179221
if owning_team:
180-
self.set_team_permission(repo_name, owning_team, "admin")
222+
try:
223+
self.set_team_permission(repo_name, owning_team, "admin")
224+
except requests.exceptions.HTTPError as perm_error:
225+
logger.warning(f"Failed to set team permission: {str(perm_error)}")
181226

182227
return repo
183228
raise
@@ -415,10 +460,37 @@ def set_team_permission(self, repo_name: str, team_name: str, permission: str) -
415460
team_name: Name of the team
416461
permission: Permission level ('pull', 'push', 'admin', 'maintain', 'triage')
417462
"""
418-
url = f"/orgs/{self.org_name}/teams/{team_name}/repos/{self.org_name}/{repo_name}"
419-
self._request("PUT", url, json={"permission": permission})
420-
421-
logger.info(f"Set {team_name} permission on {repo_name} to {permission}")
463+
# First check if the team exists
464+
try:
465+
team_url = f"/orgs/{self.org_name}/teams/{team_name}"
466+
team = self._request("GET", team_url)
467+
logger.info(f"Found team: {team_name}")
468+
469+
# Try to set permissions using the correct endpoint
470+
# Different GitHub Enterprise versions might support different API paths
471+
try:
472+
# First try the standard endpoint
473+
url = f"/orgs/{self.org_name}/teams/{team_name}/repos/{self.org_name}/{repo_name}"
474+
self._request("PUT", url, json={"permission": permission})
475+
logger.info(f"Set {team_name} permission on {repo_name} to {permission}")
476+
except requests.exceptions.HTTPError as e:
477+
if e.response.status_code == 422 or e.response.status_code == 404:
478+
# Try alternative endpoint format for older GitHub Enterprise versions
479+
try:
480+
alt_url = f"/teams/{team['id']}/repos/{self.org_name}/{repo_name}"
481+
self._request("PUT", alt_url, json={"permission": permission})
482+
logger.info(f"Set {team_name} permission on {repo_name} to {permission} using alternative endpoint")
483+
except requests.exceptions.HTTPError as alt_e:
484+
logger.error(f"Failed to set team permission using alternative endpoint: {str(alt_e)}")
485+
raise
486+
else:
487+
raise
488+
except requests.exceptions.HTTPError as e:
489+
logger.error(f"Failed to find team {team_name}: {str(e)}")
490+
if e.response.status_code == 404:
491+
logger.warning(f"Team {team_name} not found, skipping permission assignment")
492+
else:
493+
raise
422494

423495
def update_repository_topics(self, repo_name: str, topics: List[str]) -> None:
424496
"""Update the topics of a repository.

0 commit comments

Comments
 (0)