"""Record runtime checkout identity separately from the reviewed source origin.""" import subprocess def checkout_provenance(repo): """Require a Git checkout; never label changed-source runs as historical evidence.""" revision = subprocess.check_output( ["git", "-C", str(repo), "rev-parse", "HEAD"], text=True ).strip() dirty = subprocess.check_output( ["git", "-C", str(repo), "status", "--porcelain", "--untracked-files=normal"], text=True, ) return dict(checkout_revision=revision, checkout_dirty=bool(dirty)) class HistoryUnavailable(RuntimeError): """The clone cannot show the recorded commit (no git, shallow clone, installed copy).""" def historical_sha256(repo, commit, paths): """SHA-256 of each path as stored in `commit`, read with git, never from the working tree. Raises HistoryUnavailable when git or the commit object is missing, so callers can report an unchecked integrity record explicitly instead of passing or failing silently. """ import hashlib git = ["git", "-C", str(repo)] try: subprocess.run([*git, "cat-file", "-e", f"{commit}^{{commit}}"], check=True, capture_output=True) except (OSError, subprocess.CalledProcessError) as error: raise HistoryUnavailable(f"commit {commit} is not available in {repo}") from error digests = {} for path in paths: try: blob = subprocess.run([*git, "show", f"{commit}:{path}"], check=True, capture_output=True).stdout except subprocess.CalledProcessError as error: raise ValueError(f"{path} is not part of {commit}") from error digests[path] = hashlib.sha256(blob).hexdigest() return digests def historical_tree(repo, commit, prefix="src"): """Tracked file names under `prefix` at `commit` (HistoryUnavailable if missing).""" historical_sha256(repo, commit, []) return subprocess.check_output(["git", "-C", str(repo), "ls-tree", "-r", "--name-only", commit, "--", prefix], text=True).splitlines()