refactor(ops): add operations/cache.py + operations/host.py seam (W2)

Extracts pure, click-free cargo-cache status/purge and host disk/runner/
backup-template logic into operations/, mirroring the W1 pattern. Routes
commands/retention.py::_purge_cargo_cache through operations.cache while
preserving identical CLI output. commands/monitor.py is left untouched
(its refactor is optional per the plan; the Windows/Linux polymorphic
_service_status and the disk not-found detection don't map cleanly onto
the new pure functions without risking behavioural drift).
This commit is contained in:
2026-07-23 17:22:57 +02:00
parent f0d856b644
commit 00ec8a33b0
5 changed files with 764 additions and 14 deletions
+8 -14
View File
@@ -14,8 +14,8 @@ from pathlib import Path
import click
from ci_orchestrator.cargo_cache import CargoCacheStore
from ci_orchestrator.config import Config, load_config
from ci_orchestrator.operations import cache as ops_cache
@click.group()
@@ -137,7 +137,7 @@ def _purge_old_dirs(base: Path, label: str, cutoff: datetime, *, what_if: bool)
def _purge_cargo_cache(config: Config, effective_days: int, *, what_if: bool) -> None:
"""Purge stale cargo cache entries.
"""Purge stale cargo cache entries via :mod:`operations.cache`.
Entries are the heaviest thing this policy governs -- hundreds of megabytes
each, one per Cargo.lock change -- so leaving them out would repeat TODO
@@ -153,25 +153,19 @@ def _purge_cargo_cache(config: Config, effective_days: int, *, what_if: bool) ->
days = min(effective_days, config.cargo_cache.retention_days)
cutoff = datetime.now(tz=UTC) - timedelta(days=days)
store = CargoCacheStore(config.cargo_cache.root)
# Size the victims first: after a real prune the files are gone and their
# bytes are unreadable, which is how a policy ends up reporting 0.0 GB
# freed while doing real work (TODO 2.9).
doomed = store.prune_older_than(cutoff, what_if=True)
if not doomed:
result = ops_cache.purge_cargo_cache(config, retention_days=days, dry_run=what_if)
if result.removed_entries == 0:
click.echo(
f"[RetentionPolicy] cargo cache: nothing to purge "
f"(cutoff: {cutoff.strftime('%Y-%m-%d')})."
)
return
freed_mb = sum(p.stat().st_size for p in doomed if p.exists()) / 1_048_576
if not what_if:
store.prune_older_than(cutoff)
freed_mb = result.freed_bytes / 1_048_576
verb = "WhatIf: would purge" if what_if else "Purged"
click.echo(
f"[RetentionPolicy] {verb} {len(doomed)} cargo cache "
f"{'entry' if len(doomed) == 1 else 'entries'} ({freed_mb:.0f} MB)"
f"[RetentionPolicy] {verb} {result.removed_entries} cargo cache "
f"{'entry' if result.removed_entries == 1 else 'entries'} ({freed_mb:.0f} MB)"
)