Files
bot-bottle/claude_bottle/cli/cleanup.py
T
didericis aee249f119
test / unit (pull_request) Successful in 17s
test / integration (pull_request) Successful in 1m9s
refactor(cleanup): compose-ls driven, plus orphan state-dir reaping
PRD 0018 chunk 4. `claude-bottle cleanup` now derives its work
from `docker compose ls --all --format json`, filtered to projects
whose name starts with `claude-bottle-`. Per project: one `compose
down --volumes` removes the containers + the compose-managed
networks atomically.

The plan also enumerates three fallback buckets:

  - Stray containers — `claude-bottle-*` containers with no
    `com.docker.compose.project` label (left over from pre-compose
    code paths). Cleared via `docker rm -f`.
  - Stray networks — `claude-bottle-*` networks with no compose
    project label. Cleared via `docker network rm`.
  - Orphan state dirs — per-bottle `~/.claude-bottle/state/<id>/`
    dirs with no live project AND no `.preserve` marker. The
    `.preserve` marker (capability-block or auto-preserve-on-crash)
    explicitly opts-out of reaping; manual `rm -rf` is the only
    path for preserved state.

cli/cleanup.py collapses to a single y/N prompt — backend.prepare_cleanup
returns everything in one plan, backend.cleanup processes everything,
no more double-prompt for state. The CLI-side state-dir enumeration
+ `_state_summary` flags from PR #25 are gone; the backend's
orphan-detection rules subsume them.
2026-05-25 23:48:02 -04:00

48 lines
1.3 KiB
Python

"""cleanup: stop and remove all orphaned claude-bottle resources.
PRD 0018 chunk 4: backend's prepare_cleanup carries everything in
one plan — live compose projects (whose `compose down` removes
containers + networks atomically), legacy stray containers/networks
that aren't in any project, and orphan state dirs (per-bottle
state with no live project AND no `.preserve` marker). One prompt,
one cleanup call.
State dirs with `.preserve` are intentionally never touched — they
hold capability-block rebuilds or crash snapshots the operator may
want to `resume`. Manual `rm -rf ~/.claude-bottle/state/<identity>`
is the path for those.
"""
from __future__ import annotations
import sys
from ..backend import get_bottle_backend
from ..log import info
from ._common import read_tty_line
def cmd_cleanup(_argv: list[str]) -> int:
backend = get_bottle_backend()
plan = backend.prepare_cleanup()
if plan.empty:
info("no claude-bottle resources to clean up")
return 0
plan.print()
if not _prompt_yes("remove all of the above?"):
info("cleanup: skipped")
return 0
backend.cleanup(plan)
info("cleanup: done")
return 0
def _prompt_yes(message: str) -> bool:
sys.stderr.write(f"claude-bottle: {message} [y/N] ")
sys.stderr.flush()
reply = read_tty_line()
return reply in ("y", "Y", "yes", "YES")