"""Compute the per-(owner, repo) Forgejo cache file path. Single source of truth for the cache-file convention shared between three callers that must agree on the path: - ``.opencode/telemetry/server.py`` — reads the cache (Cost / PR tabs). - ``tools/_pipeline_cache.py`` — writes the cache (the scrapers). - ``tools/run-controller-state-machine-pipeline.sh`` — validates the db-mode marker before the writers open it. Convention ---------- The canonical pair ``(cleveragents, cleveragents-core)`` uses the legacy filename ``tools/.cache/forgejo.sqlite`` — kept for historical compatibility with caches the dispatcher era wrote before this partitioning existed. Any other (owner, repo) is stored as ``tools/.cache/forgejo...sqlite`` with non-alphanumeric characters folded to ``-``. The function is intentionally pure (no env reads, no filesystem checks) so it can be unit-tested trivially and called from any context. CLI --- Invoked from the controller launcher as:: python3 tools/_cache_path.py --repo-root --owner --repo Prints the absolute path to stdout. No newline-trimming gotchas — caller uses ``$(…)``. """ from __future__ import annotations import argparse import re from pathlib import Path # Kept here, NOT in server.py, so the helper has no import-time # dependency on the telemetry module's heavy startup machinery. CANONICAL_OWNER = "cleveragents" CANONICAL_REPO = "cleveragents-core" def cache_path_for(repo_root: Path, owner: str, repo: str) -> Path: """Return the SQLite cache file path for ``(owner, repo)``. Pure function. Does not check whether the file or directory exists. """ cache_dir = repo_root / "tools" / ".cache" if owner == CANONICAL_OWNER and repo == CANONICAL_REPO: return cache_dir / "forgejo.sqlite" safe_owner = re.sub(r"[^a-zA-Z0-9._-]+", "-", owner) safe_repo = re.sub(r"[^a-zA-Z0-9._-]+", "-", repo) return cache_dir / f"forgejo.{safe_owner}.{safe_repo}.sqlite" def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description=( "Print the per-(owner, repo) Forgejo cache file path. Single " "source of truth shared by server.py, _pipeline_cache.py, and " "the controller launcher." ), ) parser.add_argument("--repo-root", required=True, type=Path) parser.add_argument("--owner", required=True) parser.add_argument("--repo", required=True) args = parser.parse_args(argv) print(cache_path_for(args.repo_root.resolve(), args.owner, args.repo)) return 0 if __name__ == "__main__": raise SystemExit(main())