# retoor from devplacepy.cli._shared import _audit_cli def cmd_containers_list(args): from devplacepy.services.containers import store instances = store.all_instances() if not instances: print("No container instances") return for inst in instances: print( f"{inst['uid'][:8]} {inst.get('name', ''):24.24} {inst.get('status', ''):10} " f"desired={inst.get('desired_state', '')} policy={inst.get('restart_policy', '')}" ) def cmd_containers_reconcile(args): import asyncio from devplacepy.services.containers.service import ContainerService asyncio.run(ContainerService().run_once()) _audit_cli("cli.containers.reconcile", "CLI ran one container reconcile pass") print("Reconcile pass complete") def cmd_containers_prune(args): import asyncio from devplacepy.services.containers.runtime import get_backend from devplacepy.services.containers.service import ContainerService async def run(): await ContainerService().run_once() await get_backend().image_prune() asyncio.run(run()) _audit_cli("cli.containers.prune", "CLI reaped orphan containers and dangling images") print("Reaped orphans and pruned dangling images") def cmd_containers_prune_builds(args): import asyncio from devplacepy.database import db, get_table from devplacepy.services.containers.runtime import get_backend async def run(): backend = get_backend() removed = 0 if "builds" in db.tables: for build in list(get_table("builds").find()): tag = build.get("image_tag") if tag: await backend.remove_image(tag) removed += 1 for table in ("builds", "dockerfile_versions", "dockerfiles"): if table in db.tables: get_table(table).delete() return removed removed = asyncio.run(run()) _audit_cli("cli.containers.prune_builds", "CLI removed legacy images and build tables", metadata={"removed": removed}) print( f"Removed {removed} legacy per-project image(s) and cleared the dockerfiles/builds tables" ) def cmd_containers_gc_workspaces(args): import shutil from pathlib import Path from devplacepy import config from devplacepy.services.containers import store active = {inst["project_uid"] for inst in store.all_instances()} base = Path(config.CONTAINER_WORKSPACES_DIR) removed = 0 if base.is_dir(): for child in base.iterdir(): if child.is_dir() and child.name not in active: shutil.rmtree(child, ignore_errors=True) removed += 1 _audit_cli("cli.containers.gc_workspaces", f"CLI removed {removed} unused workspace dirs", metadata={"count": removed}) print( f"Removed {removed} unused workspace director{'y' if removed == 1 else 'ies'}" ) def register_containers(subparsers): containers = subparsers.add_parser("containers", help="Container manager") containers_sub = containers.add_subparsers(title="action", dest="action") containers_sub.add_parser("list", help="List container instances").set_defaults( func=cmd_containers_list ) containers_sub.add_parser("reconcile", help="Run one reconcile pass").set_defaults( func=cmd_containers_reconcile ) containers_sub.add_parser( "prune", help="Reap orphan containers and dangling images" ).set_defaults(func=cmd_containers_prune) containers_sub.add_parser( "prune-builds", help="Remove legacy per-project images and clear the dockerfiles/builds tables", ).set_defaults(func=cmd_containers_prune_builds) containers_sub.add_parser( "gc-workspaces", help="Remove workspace dirs with no instances" ).set_defaults(func=cmd_containers_gc_workspaces)