proof-aware-crypto-tooling-.../src/pacta/repo.py
mrwulf 5b0158ecef Machine protection: route all pacta Lean compiles through lean-guard
pacta's replay invoked `lake env lean` bare - on the reference machine
that is exactly the pattern that once OOM-crashed the host (see the
corpus' POSTMORTEM). New RepoConfig.lean_guard (set for all five repos
in examples/repos.yaml: verification/lean-guard): when configured,
every compile and axiom audit runs `lake env <guard> <file> --root=...`
instead of bare lean - hard memory cap via systemd scope + lean -M,
core pinning, timeout, single-flight lock, free-RAM preflight with the
Guard-3a retry ladder, all tuned via LEAN_MEM_MB / LEAN_MIN_FREE_MB /
LEAN_MEM_WAIT_SEC / LEAN_TIMEOUT / LEAN_MAX_CORES. Provider
attestations now record a machine_protection block naming the guard
(or "UNGUARDED"). Smoke-tested live on the real dalek repo: clamping
trace visible, compile green. 49/49 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:42:59 +02:00

83 lines
2.7 KiB
Python

from __future__ import annotations
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from .config import RepoConfig
@dataclass(slots=True)
class RepoStatus:
name: str
url: str | None
local_path: Path
exists: bool
verification_dir: Path
verification_exists: bool
commit: str | None
def configured_local_path(repo: RepoConfig, base_dir: str | Path = "repos") -> Path:
return Path(base_dir) / repo.name
def status_for(repo: RepoConfig, base_dir: str | Path = "repos", explicit_path: str | Path | None = None) -> RepoStatus:
local_path = Path(explicit_path) if explicit_path else configured_local_path(repo, base_dir)
verification_dir = local_path / repo.verification_dir
return RepoStatus(
name=repo.name,
url=repo.url,
local_path=local_path,
exists=local_path.exists(),
verification_dir=verification_dir,
verification_exists=verification_dir.exists(),
commit=git_commit(local_path) if local_path.exists() else None,
)
def resolve_lean_guard(lean_guard: str | None, repo_path: str | Path) -> str | None:
"""Resolve a repo-relative lean-guard path; None when unset or missing
(callers then run unguarded, which is only acceptable for tiny fixtures)."""
if not lean_guard:
return None
candidate = Path(lean_guard).expanduser()
if not candidate.is_absolute():
candidate = Path(repo_path) / candidate
return str(candidate.resolve()) if candidate.exists() else None
def git_commit(path: str | Path) -> str | None:
git = shutil.which("git")
if not git:
return None
try:
completed = subprocess.run(
[git, "-C", str(path), "rev-parse", "HEAD"],
check=False,
capture_output=True,
text=True,
timeout=10,
)
except (OSError, subprocess.TimeoutExpired):
return None
if completed.returncode != 0:
return None
return completed.stdout.strip() or None
def clone_or_fetch(repo: RepoConfig, base_dir: str | Path = "repos", fetch: bool = False) -> RepoStatus:
if not repo.url:
raise ValueError(f"{repo.name} has no URL")
git = shutil.which("git")
if not git:
raise RuntimeError("git is not available")
local_path = configured_local_path(repo, base_dir)
local_path.parent.mkdir(parents=True, exist_ok=True)
if local_path.exists():
if fetch:
subprocess.run([git, "-C", str(local_path), "fetch", "--all", "--prune"], check=True, timeout=120)
else:
subprocess.run([git, "clone", repo.url, str(local_path)], check=True, timeout=300)
return status_for(repo, base_dir)