"""Workspace-independent authority/network/Gitea ref sentinel.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path import subprocess from typing import Callable, Mapping class SentinelError(RuntimeError): pass @dataclass(frozen=True, slots=True) class SentinelInputs: authority_git_dir: Path gitea_remote: str gitea_api: str ref: str expected_oid: str @dataclass(frozen=True, slots=True) class SentinelResult: authority_oid: str | None network_oid: str | None gitea_oid: str | None equal: bool error: str | None = None def authority_oid(inputs: SentinelInputs) -> str: result = subprocess.run( ["git", "--git-dir", str(inputs.authority_git_dir), "rev-parse", inputs.ref], text=True, capture_output=True, check=False, ) if result.returncode != 0: raise SentinelError("AUTHORITY_REF_UNAVAILABLE") return result.stdout.strip() def evaluate( inputs: SentinelInputs, *, network_reader: Callable[[str, str], str | None], gitea_reader: Callable[[str, str], str | None], ) -> SentinelResult: try: local = authority_oid(inputs) network = network_reader(inputs.gitea_remote, inputs.ref) gitea = gitea_reader(inputs.gitea_api, inputs.ref) equal = local == network == gitea == inputs.expected_oid return SentinelResult(local, network, gitea, equal, None if equal else "OID_MISMATCH") except SentinelError as exc: return SentinelResult(None, None, None, False, str(exc))