Skip to content

Commit 44ff756

Browse files
committed
fix: handle unexpected API errors in immutable release settings fetch
Distinguish 404 (feature not enabled) from genuine errors (5xx, 403) in _fill_immutable_releases, and gracefully degrade when the org-level immutable releases endpoint returns 404 instead of raising RuntimeError. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Mikaël Barbero <mikael.barbero@eclipse-foundation.org>
1 parent 3d2d982 commit 44ff756

4 files changed

Lines changed: 49 additions & 2 deletions

File tree

otterdog/providers/github/rest/org_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ async def _get_immutable_releases_settings(self, org_id: str) -> dict[str, Any]:
139139
try:
140140
return await self.requester.request_json("GET", f"/orgs/{org_id}/settings/immutable-releases")
141141
except GitHubException as ex:
142+
if ex.status == 404:
143+
_logger.debug("immutable releases not available for org '%s', treating as disabled", org_id)
144+
return {"enforced_repositories": "none"}
142145
raise RuntimeError(f"failed retrieving immutable release settings for org '{org_id}':\n{ex}") from ex
143146

144147
async def _update_immutable_releases_settings(self, org_id: str, data: dict[str, Any]) -> None:

otterdog/providers/github/rest/repo_client.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,15 @@ async def _update_vulnerability_alerts(self, org_id: str, repo_name: str, vulner
612612
async def _fill_immutable_releases(self, org_id: str, repo_name: str, repo_data: dict[str, Any]) -> None:
613613
_logger.debug("retrieving immutable release settings for '%s/%s'", org_id, repo_name)
614614

615-
status, _ = await self.requester.request_raw("GET", f"/repos/{org_id}/{repo_name}/immutable-releases")
616-
repo_data["immutable_releases_enabled"] = status == 200
615+
status, body = await self.requester.request_raw("GET", f"/repos/{org_id}/{repo_name}/immutable-releases")
616+
if status == 200:
617+
repo_data["immutable_releases_enabled"] = True
618+
elif status == 404:
619+
repo_data["immutable_releases_enabled"] = False
620+
else:
621+
raise RuntimeError(
622+
f"failed retrieving immutable release settings for repo '{org_id}/{repo_name}': {status}: {body}"
623+
)
617624

618625
async def _update_immutable_releases(self, org_id: str, repo_name: str, immutable_releases_enabled: bool) -> None:
619626
_logger.debug("updating immutable release settings for '%s/%s'", org_id, repo_name)

tests/providers/github/rest/test_org_client.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,31 @@ async def mock_get_simple_repo_data(org_id, repo_name):
9999
},
100100
)
101101

102+
async def test_get_settings_falls_back_when_immutable_releases_not_available(self):
103+
async def mock_request_json(method, url):
104+
if url == "/orgs/org":
105+
return {"billing_email": "billing@example.com"}
106+
if url == "/orgs/org/settings/immutable-releases":
107+
raise GitHubException(url, 404, "Not Found")
108+
109+
raise AssertionError(f"unexpected call {method} {url}")
110+
111+
mock_requester = pretend.stub(request_json=pretend.call_recorder(mock_request_json))
112+
mock_restapi = pretend.stub(requester=mock_requester)
113+
org_client = OrgClient(mock_restapi)
114+
115+
result = await org_client.get_settings(
116+
"org",
117+
{
118+
"billing_email",
119+
"immutable_releases_enforced_repositories",
120+
"immutable_releases_selected_repositories",
121+
},
122+
)
123+
124+
assert result["immutable_releases_enforced_repositories"] == "none"
125+
assert result["immutable_releases_selected_repositories"] == []
126+
102127

103128
class TestOrgClientForkPrApprovalPolicy:
104129
async def test_get_fork_pr_approval_policy_success(self):

tests/providers/github/rest/test_repo_client.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ async def mock_request_raw(method, url):
199199
]
200200
assert repo_data["immutable_releases_enabled"] is False
201201

202+
async def test_fill_immutable_releases_raises_on_unexpected_status(self):
203+
async def mock_request_raw(method, url):
204+
return (500, "Internal Server Error")
205+
206+
mocked_restapi = pretend.stub(requester=pretend.stub(request_raw=pretend.call_recorder(mock_request_raw)))
207+
repo_client = RepoClient(mocked_restapi)
208+
209+
repo_data = {}
210+
211+
with pytest.raises(RuntimeError, match="failed retrieving immutable release settings"):
212+
await repo_client._fill_immutable_releases("test-org", "test-repo", repo_data)
213+
202214

203215
class TestRepoClientForkPrApprovalPolicy:
204216
async def test_get_fork_pr_approval_policy_success(self):

0 commit comments

Comments
 (0)