Skip to content

Commit 75a101d

Browse files
authored
fix(cli): show friendly message on network/timeout errors (#1311)
Previously, httpx transport errors (ConnectTimeout, ReadTimeout, ConnectError, ...) bubbled up from main() as full tracebacks. Catch httpx.RequestError in the top-level handler and print a one-line error with the URL — distinguishing timeouts from other transport failures — and exit with the existing MERGIFY_API_ERROR / GITHUB_API_ERROR code based on the request URL.
1 parent 4d2e81d commit 75a101d

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

mergify_cli/cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from mergify_cli import VERSION
2626
from mergify_cli import console
27+
from mergify_cli import console_error
2728
from mergify_cli import utils
2829
from mergify_cli.ci import cli as ci_cli_mod
2930
from mergify_cli.config import cli as config_cli_mod
@@ -87,6 +88,18 @@ def main() -> None:
8788
if str(e.request.url).startswith(utils.get_mergify_api_url()):
8889
raise SystemExit(ExitCode.MERGIFY_API_ERROR) from None
8990
raise SystemExit(ExitCode.GITHUB_API_ERROR) from None
91+
except httpx.RequestError as e:
92+
url = str(e.request.url)
93+
if isinstance(e, httpx.TimeoutException):
94+
console_error(
95+
f"timed out contacting {url}. "
96+
"Check your network connection or try again later.",
97+
)
98+
else:
99+
console_error(f"network error contacting {url}: {e}")
100+
if url.startswith(utils.get_mergify_api_url()):
101+
raise SystemExit(ExitCode.MERGIFY_API_ERROR) from None
102+
raise SystemExit(ExitCode.GITHUB_API_ERROR) from None
90103
except utils.CommandError as e:
91104
console.print(f"error: {e}", style="red")
92105
raise SystemExit(ExitCode.GENERIC_ERROR) from None

mergify_cli/tests/test_cli.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from unittest import mock
44

55
from click import testing
6+
import httpx
67
import pytest
78

89
from mergify_cli import cli as cli_mod
@@ -69,6 +70,60 @@ def fail_cmd() -> None:
6970
assert result.exit_code == ExitCode.GENERIC_ERROR, result.output
7071

7172

73+
def test_cli_connect_timeout_shows_clean_message(
74+
capsys: pytest.CaptureFixture[str],
75+
) -> None:
76+
"""httpx.ConnectTimeout to GitHub produces a friendly message, no traceback."""
77+
request = httpx.Request("GET", "https://api.github.com/user")
78+
error = httpx.ConnectTimeout("timeout", request=request)
79+
with (
80+
mock.patch.object(cli_mod, "cli", side_effect=error),
81+
pytest.raises(SystemExit) as exc_info,
82+
):
83+
cli_mod.main()
84+
85+
assert exc_info.value.code == ExitCode.GITHUB_API_ERROR
86+
out = capsys.readouterr().out
87+
assert "timed out" in out
88+
assert "https://api.github.com/user" in out
89+
assert "Traceback" not in out
90+
assert "ConnectTimeout" not in out
91+
92+
93+
def test_cli_connect_timeout_to_mergify_api(
94+
capsys: pytest.CaptureFixture[str],
95+
) -> None:
96+
"""Timeouts against the Mergify API map to MERGIFY_API_ERROR."""
97+
request = httpx.Request("GET", f"{utils.get_mergify_api_url()}/v1/foo")
98+
error = httpx.ConnectTimeout("timeout", request=request)
99+
with (
100+
mock.patch.object(cli_mod, "cli", side_effect=error),
101+
pytest.raises(SystemExit) as exc_info,
102+
):
103+
cli_mod.main()
104+
105+
assert exc_info.value.code == ExitCode.MERGIFY_API_ERROR
106+
assert "timed out" in capsys.readouterr().out
107+
108+
109+
def test_cli_connect_error_shows_clean_message(
110+
capsys: pytest.CaptureFixture[str],
111+
) -> None:
112+
"""Non-timeout transport errors also get a friendly message."""
113+
request = httpx.Request("GET", "https://api.github.com/user")
114+
error = httpx.ConnectError("connection refused", request=request)
115+
with (
116+
mock.patch.object(cli_mod, "cli", side_effect=error),
117+
pytest.raises(SystemExit) as exc_info,
118+
):
119+
cli_mod.main()
120+
121+
assert exc_info.value.code == ExitCode.GITHUB_API_ERROR
122+
out = capsys.readouterr().out
123+
assert "network error" in out
124+
assert "connection refused" in out
125+
126+
72127
def test_main_entrypoint_handles_mergify_error(
73128
monkeypatch: pytest.MonkeyPatch,
74129
) -> None:

0 commit comments

Comments
 (0)