Files
ss-tools/scripts/write_source_repository_identity.py

71 lines
3.1 KiB
Python

#!/usr/bin/env python3
# #region Tooling.SourceRepositoryIdentity [C:4] [TYPE Module] [SEMANTICS build,source,origin,immutable]
# @BRIEF Observe one unambiguous fetch/push origin; emit credential-free current metadata.
# @POST Dirty checkout is allowed; missing, ambiguous, or mismatched fetch/push origin refuses metadata.
# @RATIONALE Build-time origin observation replaces unavailable container Git, not runtime source discovery.
# @REJECTED Reusing stale files or guessing a repository from destination configuration cannot prove source identity.
import argparse
from dataclasses import asdict
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
OWNER = Path(__file__).resolve().parents[1] / "backend/src/core/source_repository_identity.py"
SPEC = importlib.util.spec_from_file_location("source_repository_identity_build", OWNER)
owner = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = owner
SPEC.loader.exec_module(owner)
# #region Tooling.SourceRepositoryIdentity.Git [C:2] [TYPE Function]
def git(checkout, *args):
result = subprocess.run(["git", "-C", str(checkout), *args], capture_output=True, text=True, timeout=15)
if result.returncode:
raise owner.SourceRepositoryRefusal()
return result.stdout.strip()
# #endregion Tooling.SourceRepositoryIdentity.Git
# #region Tooling.SourceRepositoryIdentity.Observe [C:4] [TYPE Function]
def observe(checkout):
try:
root = Path(git(checkout, "rev-parse", "--show-toplevel")).resolve(strict=True)
if root != Path(checkout).resolve(strict=True):
raise owner.SourceRepositoryRefusal()
fetch = git(root, "config", "--get-all", "remote.origin.url").splitlines()
effective_fetch = git(root, "remote", "get-url", "--all", "origin").splitlines()
push = git(root, "remote", "get-url", "--push", "--all", "origin").splitlines()
if len(fetch) != 1 or len(effective_fetch) != 1 or len(push) != 1:
raise owner.SourceRepositoryRefusal()
origin = owner.normalize_repository_url(fetch[0])
if owner.normalize_repository_url(effective_fetch[0]) != origin or owner.normalize_repository_url(push[0]) != origin:
raise owner.SourceRepositoryRefusal()
value = {"origin": asdict(origin)}
owner.decode_source_identity(json.dumps(value))
return value
except (OSError, RuntimeError, subprocess.SubprocessError):
raise owner.SourceRepositoryRefusal() from None
# #endregion Tooling.SourceRepositoryIdentity.Observe
# #region Tooling.SourceRepositoryIdentity.Main [C:2] [TYPE Function]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--checkout", required=True, type=Path)
args = parser.parse_args()
try:
value = observe(args.checkout)
except owner.SourceRepositoryRefusal as failure:
print(failure.code, file=sys.stderr)
return 2
print(json.dumps(value, sort_keys=True, separators=(",", ":")))
return 0
# #endregion Tooling.SourceRepositoryIdentity.Main
if __name__ == "__main__":
sys.exit(main())
# #endregion Tooling.SourceRepositoryIdentity