aee249f119
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.
60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
"""DockerBottleCleanupPlan — concrete subclass of BottleCleanupPlan.
|
|
|
|
PRD 0018 chunk 4: cleanup is centered on compose projects. `docker
|
|
compose ls` is the source of truth for what's running; the plan
|
|
carries the projects to `compose down`, plus three fallback buckets
|
|
for legacy / orphan resources:
|
|
|
|
- stray_containers: pre-compose `claude-bottle-*` containers not
|
|
attached to any compose project. Cleared via `docker rm -f`.
|
|
- stray_networks: same idea for networks. Cleared via
|
|
`docker network rm`.
|
|
- orphan_state_dirs: per-bottle state dirs under
|
|
~/.claude-bottle/state/ that have no live compose project AND
|
|
no `.preserve` marker. Reaped via `shutil.rmtree`.
|
|
|
|
Compose-managed networks are removed by `compose down --volumes`,
|
|
so they don't appear in stray_networks for a normal project — only
|
|
truly leftover ones.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from dataclasses import dataclass
|
|
|
|
from ...log import info
|
|
from .. import BottleCleanupPlan
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DockerBottleCleanupPlan(BottleCleanupPlan):
|
|
"""Resources DockerBottleBackend.cleanup will remove. Produced by
|
|
`prepare_cleanup`; sorted so the y/N output is stable."""
|
|
|
|
projects: tuple[str, ...]
|
|
stray_containers: tuple[str, ...]
|
|
stray_networks: tuple[str, ...]
|
|
orphan_state_dirs: tuple[str, ...]
|
|
|
|
@property
|
|
def empty(self) -> bool:
|
|
return (
|
|
not self.projects
|
|
and not self.stray_containers
|
|
and not self.stray_networks
|
|
and not self.orphan_state_dirs
|
|
)
|
|
|
|
def print(self) -> None:
|
|
print(file=sys.stderr)
|
|
for name in self.projects:
|
|
info(f"compose project: {name}")
|
|
for name in self.stray_containers:
|
|
info(f"stray container: {name}")
|
|
for name in self.stray_networks:
|
|
info(f"stray network: {name}")
|
|
for name in self.orphan_state_dirs:
|
|
info(f"orphan state: {name}")
|
|
print(file=sys.stderr)
|