fix(cli): stop --print-effective-config from starting a container

argparse requires exactly one action, so the documented diagnostic form
`--print-effective-config --config … --start` never took the early-return
branch in main.run(): it printed the resolved config and then executed a
real do_start(), silently replacing a running container with the [default]
model. README, installation guide and manual all recommended that form.

Make it an action in the mutually exclusive group. It can no longer be
combined with --start/--check/--stop/--change/--chat (argparse error,
exit 2), and it skips the docker_available() check, so it now really is
the offline config check the docs promise. --dry-run remains a modifier
and still requires a reachable daemon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-07-10 16:25:07 +02:00
commit 3e3dd1c150
8 changed files with 104 additions and 30 deletions

57
tests/test_main.py Normal file
View file

@ -0,0 +1,57 @@
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from llamacppctl import actions, main as main_mod # noqa: E402
CONFIG = """
[default]
hf_home = /srv/models
model_path = qwen3/default.gguf
container_name = test_main
"""
def _config(tmp_path: Path) -> Path:
path = tmp_path / "llama.cpp.config"
path.write_text(CONFIG, encoding="utf-8")
return path
def test_print_effective_config_has_no_side_effects(tmp_path, monkeypatch, capsys):
"""Regression: --print-effective-config used to fall through into do_start()
because argparse always required an action, silently replacing a running
container."""
started = []
monkeypatch.setattr(actions, "do_start", lambda *a, **k: started.append(1))
monkeypatch.setattr(actions, "do_change", lambda *a, **k: started.append(1))
monkeypatch.setattr(actions, "do_stop", lambda *a, **k: started.append(1))
rc = main_mod.run(["--print-effective-config", "--config", str(_config(tmp_path))])
assert rc == 0
assert started == []
assert '"container_name": "test_main"' in capsys.readouterr().out
def test_print_effective_config_works_without_docker(tmp_path, monkeypatch, capsys):
"""It is a pure config check, so it must not require a reachable daemon."""
def boom() -> bool:
raise AssertionError("docker_available() must not be consulted")
monkeypatch.setattr(main_mod, "docker_available", boom)
rc = main_mod.run(["--print-effective-config", "--config", str(_config(tmp_path))])
assert rc == 0
assert '"hf_home": "/srv/models"' in capsys.readouterr().out
def test_start_still_requires_docker(tmp_path, monkeypatch, capsys):
monkeypatch.setattr(main_mod, "docker_available", lambda: False)
rc = main_mod.run(["--start", "--config", str(_config(tmp_path))])
assert rc == 1
assert "docker is not available" in capsys.readouterr().err