0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-08 14:40:05 +00:00

Update the GitHub transport to handle uninstalled GitHub app

If the user uninstalls our GitHub app from their GitHub account then,
the next time we request an access token, we will receive a
HTTP 404 response from GitHub. Rather than throwing an unhandled
exception, Healthchecks will now show an error message to the
user saying "GitHub denied access to reposuch/andsuch".
This commit is contained in:
Pēteris Caune 2025-03-14 14:09:07 +02:00
parent 74b7860a0d
commit df801c6796
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
3 changed files with 16 additions and 0 deletions

View file

@ -132,6 +132,15 @@ class NotifyGitHubTestCase(BaseTestCase):
n = Notification.objects.get()
self.assertEqual(n.error, "Received status code 400")
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_access_token(self, mock_post: Mock) -> None:
with patch("hc.api.transports.github") as mock:
mock.get_installation_access_token.return_value = None
self.channel.notify(self.flip)
n = Notification.objects.get()
self.assertEqual(n.error, "GitHub denied access to alice/foo")
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_shows_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200

View file

@ -1695,5 +1695,8 @@ class GitHub(HttpTransport):
inst_id = self.channel.github.installation_id
token = github.get_installation_access_token(inst_id)
if token is None:
raise TransportError(f"GitHub denied access to {self.channel.github.repo}")
headers = {"Authorization": f"Bearer {token}"}
self.post(url, json=payload, headers=headers)

View file

@ -104,5 +104,9 @@ def get_installation_access_token(installation_id: int) -> str:
encoded = jwt.encode(payload, settings.GITHUB_PRIVATE_KEY, algorithm="RS256")
url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
result = curl.post(url, headers={"Authorization": f"Bearer {encoded}"})
if result.status_code == 404:
# The installation does not exist (our GitHub app has been uninstalled)
return None
doc = AccessTokensResponse.model_validate_json(result.content, strict=True)
return doc.token