nima1's picture
Publish verified checkpoint and losslessly compressed study evidence
4be6a52 verified
Raw History Blame Contribute Delete
3.88 kB
"""Source identity for exact Git checkouts and published, hash-verified source bundles."""
import hashlib
import json
import re
import subprocess
from pathlib import Path
from typing import Any
SOURCE_MANIFEST = "source-manifest.json"
def file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while chunk := stream.read(1_048_576):
digest.update(chunk)
return digest.hexdigest()
def source_identity(root: Path) -> dict[str, Any]:
"""Never mistake an enclosing, unrelated Git repository for this source tree.
Published manifests identify original source and verify matching local bytes;
they are provenance records, not signed proof of the claimed commit's authorship.
"""
root = root.resolve()
try:
top = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], cwd=root, text=True, stderr=subprocess.DEVNULL
).strip()
if Path(top).resolve() == root:
commit = subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=root, text=True, stderr=subprocess.DEVNULL
).strip()
if not re.fullmatch(r"[0-9a-f]{40}", commit):
raise ValueError("source requires a full 40-character Git commit")
dirty = bool(
subprocess.check_output(
["git", "status", "--porcelain"],
cwd=root,
text=True,
stderr=subprocess.DEVNULL,
).strip()
)
return {
"source_commit": commit,
"source_dirty": dirty,
"source_identity_method": "exact-git-root",
}
except (OSError, subprocess.CalledProcessError):
pass
path = root / SOURCE_MANIFEST
if not path.is_file() or path.is_symlink():
raise ValueError(
"no exact Stackcraft Git root or bundled source-manifest.json; "
"run from the released source directory or an initialized source checkout"
)
manifest = json.loads(path.read_text())
commit = manifest.get("source_commit")
hashes = manifest.get("source_hashes")
if (
type(manifest.get("schema_version")) is not int
or manifest["schema_version"] != 1
or not isinstance(commit, str)
or not re.fullmatch(r"[0-9a-f]{40}", commit)
or type(manifest.get("source_dirty")) is not bool
or not isinstance(hashes, dict)
or not hashes
):
raise ValueError("invalid published source-manifest contract")
changed = []
for name, expected in hashes.items():
relative = Path(name)
if (
relative.is_absolute()
or ".." in relative.parts
or not relative.parts
or not isinstance(expected, str)
or not re.fullmatch(r"[0-9a-f]{64}", expected)
):
raise ValueError("invalid source manifest path or SHA256")
source = root / relative
if (
not source.is_file()
or source.is_symlink()
or not source.resolve().is_relative_to(root)
or file_sha256(source) != expected
):
changed.append(name)
# Newly added importable files can change behavior even if existing files match.
for folder in ("src", "scripts", "tests"):
for source in (root / folder).rglob("*.py"):
relative = str(source.relative_to(root))
if relative not in hashes:
changed.append(relative)
return {
"source_commit": commit,
"source_dirty": manifest["source_dirty"] or bool(changed),
"source_identity_method": "published-source-manifest",
"source_manifest_sha256": file_sha256(path),
"modified_source_files": sorted(set(changed)),
}