9f2498397f
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.
48 lines
1.3 KiB
Python
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")
|