mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
feat: add uninstall dry-run mode
Port from qwibitai/nanoclaw#2719: let operators preview the uninstall plan without stopping services or deleting files.
This commit is contained in:
parent
1deeaf71ab
commit
491689784e
3 changed files with 85 additions and 0 deletions
|
|
@ -38,4 +38,9 @@ def build_uninstall_parser(subparsers, *, cmd_uninstall: Callable) -> None:
|
|||
uninstall_parser.add_argument(
|
||||
"--yes", "-y", action="store_true", help="Skip confirmation prompts"
|
||||
)
|
||||
uninstall_parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Print what uninstall would remove without changing anything",
|
||||
)
|
||||
uninstall_parser.set_defaults(func=cmd_uninstall)
|
||||
|
|
|
|||
|
|
@ -575,6 +575,14 @@ def run_uninstall(args):
|
|||
project_root = get_project_root()
|
||||
hermes_home = get_hermes_home()
|
||||
|
||||
if bool(getattr(args, "dry_run", False)):
|
||||
_print_uninstall_dry_run(
|
||||
project_root=project_root,
|
||||
hermes_home=hermes_home,
|
||||
full_uninstall=bool(getattr(args, "full", False)),
|
||||
)
|
||||
return
|
||||
|
||||
# Detect named profiles when uninstalling from the default root —
|
||||
# offer to clean them up too instead of leaving zombie HERMES_HOMEs
|
||||
# and systemd units behind.
|
||||
|
|
@ -704,6 +712,30 @@ def run_uninstall(args):
|
|||
)
|
||||
|
||||
|
||||
def _print_uninstall_dry_run(*, project_root: Path, hermes_home: Path, full_uninstall: bool) -> None:
|
||||
"""Print the uninstall plan without stopping services or deleting files."""
|
||||
print()
|
||||
print(color("Dry run: no files, services, or environment entries will be changed.", Colors.CYAN, Colors.BOLD))
|
||||
print()
|
||||
print(color("Would inspect/remove:", Colors.YELLOW, Colors.BOLD))
|
||||
print(" • Gateway services and standalone gateway processes")
|
||||
print(" • Hermes PATH entries from shell configs / Windows User PATH")
|
||||
print(" • Hermes wrapper scripts and Hermes-managed node/npm/npx symlinks")
|
||||
print(" • Desktop Chat GUI artifacts")
|
||||
print(f" • Code checkout: {project_root}")
|
||||
if full_uninstall:
|
||||
print(f" • Hermes config/data: {hermes_home}")
|
||||
if _is_default_hermes_home(hermes_home):
|
||||
profiles = _discover_named_profiles()
|
||||
if profiles:
|
||||
print(" • Named profiles (interactive uninstall asks before removing):")
|
||||
for prof in profiles:
|
||||
print(f" - {prof.name}: {prof.path}")
|
||||
else:
|
||||
print(f" • Keep Hermes config/data: {hermes_home}")
|
||||
print()
|
||||
|
||||
|
||||
def _perform_uninstall(
|
||||
*,
|
||||
project_root: Path,
|
||||
|
|
|
|||
48
tests/hermes_cli/test_uninstall_dry_run.py
Normal file
48
tests/hermes_cli/test_uninstall_dry_run.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
from hermes_cli import uninstall
|
||||
|
||||
|
||||
def test_dry_run_prints_plan_without_mutating(monkeypatch, tmp_path, capsys):
|
||||
project_root = tmp_path / "hermes-agent"
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
project_root.mkdir()
|
||||
hermes_home.mkdir()
|
||||
(hermes_home / "config.yaml").write_text("model: {}\n")
|
||||
|
||||
called = False
|
||||
|
||||
def _fail_if_called(**kwargs):
|
||||
nonlocal called
|
||||
called = True
|
||||
|
||||
monkeypatch.setattr(uninstall, "get_project_root", lambda: project_root)
|
||||
monkeypatch.setattr(uninstall, "get_hermes_home", lambda: hermes_home)
|
||||
monkeypatch.setattr(uninstall, "_is_default_hermes_home", lambda home: False)
|
||||
monkeypatch.setattr(uninstall, "_discover_named_profiles", lambda: [])
|
||||
monkeypatch.setattr(uninstall, "_perform_uninstall", _fail_if_called)
|
||||
|
||||
uninstall.run_uninstall(SimpleNamespace(dry_run=True, yes=True, full=True))
|
||||
|
||||
output = capsys.readouterr().out
|
||||
assert called is False
|
||||
assert "Dry run" in output
|
||||
assert str(project_root) in output
|
||||
assert str(hermes_home) in output
|
||||
assert project_root.exists()
|
||||
assert hermes_home.exists()
|
||||
|
||||
|
||||
def test_build_uninstall_parser_accepts_dry_run():
|
||||
import argparse
|
||||
from hermes_cli.subcommands.uninstall import build_uninstall_parser
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
build_uninstall_parser(subparsers, cmd_uninstall=lambda args: args)
|
||||
|
||||
args = parser.parse_args(["uninstall", "--dry-run", "--full"])
|
||||
|
||||
assert args.dry_run is True
|
||||
assert args.full is True
|
||||
Loading…
Add table
Add a link
Reference in a new issue