mirror of
https://github.com/HKUDS/CLI-Anything.git
synced 2026-10-02 02:44:51 +08:00
fix: report backend errors without a traceback and keep --json parsable (#439)
`main()` called `cli()` without any error handling, so a failure to reach the
AdGuardHome API surfaced as a chained urllib3/requests/click traceback. In
`--json` mode nothing at all was written to stdout, leaving an agent with no
parsable result.
The Docker hint inside that error message also mapped the host port to itself
(`-p 3001:3001`), which cannot reach a fresh container: the image serves its web
interface on container port 3000, as the harness's own E2E fixture and README
already assume.
- catch RuntimeError and requests exceptions in `main()`: emit
`{"error": ...}` on stdout with `--json`, the plain message on stderr
otherwise, exit 1 in both cases
- map the Docker hint to container port 3000 and mention that the wizard must
keep the web interface there
- cover both paths with tests: JSON shape and exit status on an unreachable
host, plain-message path on stderr, and the port mapping in the hint
Fixes #437
Claude-Session: https://claude.ai/code/session_01ChPxA9ksnNqr83nLbfMhwu
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
af4320fce5
commit
060712a287
@@ -6,6 +6,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import requests
|
||||
|
||||
from cli_anything.adguardhome.core import blocking as blocking_core
|
||||
from cli_anything.adguardhome.core import clients as clients_core
|
||||
@@ -84,7 +85,17 @@ def cli(ctx: click.Context, host, port, username, password, config_path, use_htt
|
||||
|
||||
|
||||
def main():
|
||||
cli(obj={})
|
||||
try:
|
||||
cli(obj={})
|
||||
except (RuntimeError, requests.exceptions.RequestException) as exc:
|
||||
# Backend failures reach this point as exceptions; without this handler they
|
||||
# surface as a chained traceback and --json produces nothing parsable.
|
||||
# sys.argv is used because the Click context is already unwound here.
|
||||
if "--json" in sys.argv:
|
||||
click.echo(json.dumps({"error": str(exc)}, default=str))
|
||||
else:
|
||||
click.echo(str(exc), err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -93,6 +93,11 @@ class TestAdGuardHomeClient:
|
||||
assert result == {}
|
||||
mock_post.assert_called_once()
|
||||
|
||||
def test_connection_error_docker_hint_maps_to_container_port(self):
|
||||
client = AdGuardHomeClient(host="localhost", port=3001)
|
||||
message = str(client._connection_error(Exception("refused")))
|
||||
assert "-p 3001:3000" in message
|
||||
|
||||
def test_connection_error_raises_runtime(self):
|
||||
c = make_client()
|
||||
with patch.object(c.session, "get", side_effect=requests.exceptions.ConnectionError("refused")):
|
||||
|
||||
@@ -193,6 +193,27 @@ class TestCLISubprocess:
|
||||
assert result.returncode == 0
|
||||
assert "parental" in result.stdout
|
||||
|
||||
def test_connection_error_json_is_parsable(self):
|
||||
"""--json must stay machine-readable when the server is unreachable."""
|
||||
result = self._run(
|
||||
["--json", "--host", "127.0.0.1", "--port", "1", "server", "status"],
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
data = json.loads(result.stdout)
|
||||
assert "Cannot connect to AdGuardHome" in data["error"]
|
||||
assert "Traceback" not in result.stderr
|
||||
|
||||
def test_connection_error_human_message(self):
|
||||
"""Without --json the message goes to stderr, without a traceback."""
|
||||
result = self._run(
|
||||
["--host", "127.0.0.1", "--port", "1", "server", "status"],
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert "Cannot connect to AdGuardHome" in result.stderr
|
||||
assert "Traceback" not in result.stderr
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Docker E2E tests
|
||||
|
||||
@@ -38,7 +38,8 @@ class AdGuardHomeClient:
|
||||
f"Cannot connect to AdGuardHome at {self.base_url}.\n"
|
||||
f"Ensure AdGuardHome is running and accessible.\n"
|
||||
f"Install: curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v\n"
|
||||
f"Or Docker: docker run --name adguardhome -p {self.port}:{self.port} adguard/adguardhome\n"
|
||||
f"Or Docker: docker run --name adguardhome -p {self.port}:3000 adguard/adguardhome\n"
|
||||
f"(the container serves its web interface on port 3000 - keep that port in the setup wizard)\n"
|
||||
f"Error: {e}"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user