mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-12 05:36:40 +00:00
Move the registry-cache pull-through proxy out of the always-on `ci` profile into its own `cache` profile, and select it based on the runtime environment instead of statically. Why The proxy only earns its keep when image pulls amortize across many runs on the same host. That holds on developer machines (cross-run image dedup, persistent host disk) but not on GitHub-hosted runners (fresh disk per job), where the proxy startup overhead is net loss without the corresponding payoff. How * Compose: split `registry-cache` into its own `cache` profile; `infinito.depends_on.registry-cache` gets `required: false` so Compose does not auto-start the proxy via the dependency when the profile is inactive; the systemd drop-in bind-mount source is gated by the new `INFINITO_REGISTRY_CACHE_PROXY_CONF` env var with `/dev/null` default, so dockerd loads an empty drop-in (no `HTTP_PROXY`, no `ExecStartPre`) when the proxy is not running and pulls images directly. * New `cli/deploy/development/profile.py` `Profile` class is the SPOT for which compose profiles activate. KISS + SRP: `is_ci()` reads the three CI signals (`GITHUB_ACTIONS`, `RUNNING_ON_GITHUB`, `CI`), `registry_cache_active()` is its strict inverse, and `args()` returns the matching `--profile ...` flag list. * `Compose` and `down.py` consume the same `Profile` so up/down cannot drift on which services are part of the stack. * Documentation under `compose/registry-cache/README.md`, `docs/contributing/artefact/files/compose.yml.md` and `docs/contributing/environment/variables.md` describes the new toggle and the `INFINITO_REGISTRY_CACHE_PROXY_CONF` env var. Pre-existing documentation.md violations in those files (one SPOT self-label, one em dash, two path-as-link-text instances) corrected in the same pass per the fix-nearby rule. * Tests: 14 new `Profile` unit tests in `test_profile.py` plus two scenario tests in `test_compose.py` that lock down the `--profile ci` (CI runner) vs `--profile ci --profile cache` (local) argument shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from .common import resolve_distro
|
|
from .profile import Profile
|
|
|
|
|
|
def _repo_root_from_here() -> Path:
|
|
# Derive the repository root relative to this file.
|
|
# Adjust the number of parents if the project layout changes.
|
|
return Path(__file__).resolve().parents[3]
|
|
|
|
|
|
CI_DOCKER_ROOT = Path("/mnt/docker")
|
|
CI_DOCKER_ROOT_STR = str(CI_DOCKER_ROOT)
|
|
|
|
|
|
def _base_env(*, distro: str) -> dict[str, str]:
|
|
env = dict(os.environ)
|
|
env["INFINITO_DISTRO"] = distro
|
|
return env
|
|
|
|
|
|
def _compose_run(*, repo_root: Path, distro: str, args: list[str]) -> None:
|
|
cmd = ["docker", "compose", "--env-file", "env.ci"]
|
|
env_development = repo_root / "env.development"
|
|
if env_development.exists():
|
|
cmd += ["--env-file", "env.development"]
|
|
|
|
# Tear the registry-cache down too if it was activated for the
|
|
# corresponding `up`. Reuse the Profile decision so up/down cannot
|
|
# disagree about which services are part of the stack.
|
|
cmd += Profile().args()
|
|
cmd += list(args)
|
|
env = _base_env(distro=distro)
|
|
env.setdefault("NIX_CONFIG", "")
|
|
subprocess.run(cmd, cwd=repo_root, env=env, check=True, text=True)
|
|
|
|
|
|
def _cleanup_docker_root() -> None:
|
|
docker_root = CI_DOCKER_ROOT
|
|
|
|
if os.environ.get("RUNNING_ON_GITHUB") != "true":
|
|
print(f">>> Not on GitHub - No bind volumes will be deleted: {docker_root}")
|
|
return
|
|
|
|
docker_root_env = os.environ.get("INFINITO_DOCKER_VOLUME", "").strip().rstrip("/")
|
|
|
|
if docker_root_env and docker_root_env != CI_DOCKER_ROOT_STR:
|
|
raise RuntimeError(
|
|
"SECURITY VIOLATION: "
|
|
f"INFINITO_DOCKER_VOLUME={docker_root_env} is not allowed on GitHub runner. "
|
|
f"Only {CI_DOCKER_ROOT_STR} is permitted."
|
|
)
|
|
|
|
if not docker_root.exists():
|
|
print(f">>> Docker root does not exist, nothing to clean: {docker_root}")
|
|
return
|
|
|
|
print(f">>> CI cleanup: wiping Docker root: {docker_root}")
|
|
shutil.rmtree(docker_root, ignore_errors=True)
|
|
docker_root.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def down_stack(*, repo_root: Path, distro: str) -> None:
|
|
print(">>> Stopping compose stack and removing volumes")
|
|
try:
|
|
_compose_run(
|
|
repo_root=repo_root,
|
|
distro=distro,
|
|
args=["down", "--remove-orphans", "-v"],
|
|
)
|
|
finally:
|
|
_cleanup_docker_root()
|
|
|
|
|
|
def add_parser(sub: argparse._SubParsersAction) -> None:
|
|
p = sub.add_parser("down", help="Stop compose stack and remove volumes.")
|
|
p.set_defaults(_handler=handler)
|
|
|
|
|
|
def handler(args: argparse.Namespace) -> int:
|
|
down_stack(repo_root=_repo_root_from_here(), distro=resolve_distro())
|
|
return 0
|