Add castle doctor + getting-started docs
The README/docs explained how the system works but not how to get it up and running, and there was no way to tell a healthy node from a half-configured one. Two additions close that gap. castle doctor — a read-only preflight/postflight check. It inspects setup *and* runtime (CLI on PATH, uv, lingering; repo:, control plane registered, dashboard built; gateway/api running + listening, specs generated; and — under tls=acme — DNS-plugin Caddy, provider token, :443 bind; tunnel config for public services) and, for anything not green, prints the exact next command. Exit 0 when nothing failed (warnings allowed), 1 otherwise, so it doubles as a scriptable smoke test after install/deploy. Agents can lean on it the same way they use `castle tool list`. Docs — the quick start now leads with prerequisites and one command (install.sh installs the CLI + registers the control plane, so the old manual `uv tool install` step is gone), adds a `castle doctor` verify step, and gains an "exposure ladder" table framing the three rungs (localhost → LAN HTTPS → public) that link the deep DNS/TLS/tunnel docs. install.sh's closing summary and the AGENTS.md CLI reference mention doctor too.
This commit is contained in:
39
cli/tests/test_doctor.py
Normal file
39
cli/tests/test_doctor.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Tests for castle doctor."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.commands.doctor import run_doctor
|
||||
|
||||
|
||||
class TestDoctor:
|
||||
"""The diagnosis path — a bare, unconfigured node should fail loudly."""
|
||||
|
||||
def test_bare_node_reports_problems(self, castle_root: Path, capsys: object) -> None:
|
||||
"""No repo:, no control plane, nothing running → exit 1 with fix hints."""
|
||||
from castle_cli.config import load_config
|
||||
|
||||
# The shared fixture has no repo: and no castle-gateway/api/dashboard, so the
|
||||
# Configuration and Runtime sections must FAIL. Patch where doctor imports it.
|
||||
with patch("castle_core.config.load_config", return_value=load_config(castle_root)):
|
||||
result = run_doctor(Namespace())
|
||||
|
||||
assert result == 1
|
||||
out = capsys.readouterr().out # type: ignore[attr-defined]
|
||||
assert "repo: not set" in out
|
||||
assert "control plane missing" in out
|
||||
# Every failing check offers a concrete next command.
|
||||
assert "castle deploy" in out
|
||||
|
||||
def test_load_failure_is_first_fail(self, capsys: object) -> None:
|
||||
"""A castle.yaml that won't load is surfaced as a FAIL, not a traceback."""
|
||||
with patch("castle_core.config.load_config", side_effect=ValueError("bad yaml")):
|
||||
result = run_doctor(Namespace())
|
||||
|
||||
assert result == 1
|
||||
out = capsys.readouterr().out # type: ignore[attr-defined]
|
||||
assert "failed to load" in out
|
||||
assert "bad yaml" in out
|
||||
Reference in New Issue
Block a user