Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/github_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
class GithubClient:
# This transform GH jobs conclusion keywords to Sentry performance status
github_status_trace_status = {"success": "ok", "failure": "internal_error"}
github_api_timeout_seconds = 10

def __init__(self, token, dsn, dry_run=False) -> None:
self.token = token
Expand All @@ -42,7 +43,11 @@
def _fetch_github(self, url):
headers = {"Authorization": f"token {self.token}"}

req = requests.get(url, headers=headers)
req = requests.get(
url,
headers=headers,
timeout=self.github_api_timeout_seconds,
)

Check failure

Code scanning / CodeQL

Full server-side request forgery Critical

The full URL of this request depends on a
user-provided value
.
Comment on lines +46 to +50
req.raise_for_status()
return req

Expand Down Expand Up @@ -143,7 +148,17 @@
f"We are ignoring '{job['name']}' because it was skipped -> {job['html_url']}",
)
return
trace = self._generate_trace(job)
try:
trace = self._generate_trace(job)
except requests.exceptions.Timeout as error:
# Timeouts while reading GH metadata are usually transient.
# Dropping this single trace avoids failing the whole webhook call.
logging.warning(
"Skipping trace generation after GitHub API timeout for run %s: %s",
job.get("run_id", "unknown"),
error,
)
return
if trace:
return self._send_envelope(trace)

Expand Down
32 changes: 32 additions & 0 deletions tests/test_github_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import sys
from datetime import datetime
from unittest import mock
from unittest.mock import patch

import pytest
Expand Down Expand Up @@ -59,6 +60,37 @@ def test_ensure_raise_error_on_github_api_failure():
)


@patch("src.github_sdk.requests.get")
def test_fetch_github_sets_timeout(mock_get):
mock_response = mock.Mock()
mock_get.return_value = mock_response

url = "https://api.github.com/repos/getsentry/sentry/actions/runs/2104746951"
client = GithubClient(dsn=DSN, token=TOKEN)
response = client._fetch_github(url)

assert response == mock_response
mock_get.assert_called_once_with(
url,
headers={"Authorization": f"token {TOKEN}"},
timeout=GithubClient.github_api_timeout_seconds,
)


def test_send_trace_handles_timeout_during_metadata_fetch(jobA_job):
client = GithubClient(dsn=DSN, token=TOKEN)
with patch.object(
client,
"_generate_trace",
side_effect=requests.exceptions.Timeout("timed out"),
) as mock_generate_trace:
with patch.object(client, "_send_envelope") as mock_send_envelope:
assert client.send_trace(jobA_job) is None

mock_generate_trace.assert_called_once_with(jobA_job)
mock_send_envelope.assert_not_called()


@freeze_time()
@responses.activate
@patch("src.github_sdk.get_uuid")
Expand Down
Loading