Files
smirk-backend-core/tools/check-build-source.py
T

73 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Admit one reviewed canonical source or its exact Builds merge wrapper."""
import os
import re
import subprocess
import sys
class Refusal(ValueError):
pass
def git(*args):
return subprocess.check_output(["git", *args], text=True, stderr=subprocess.PIPE).strip()
def commit(value, label):
if not re.fullmatch(r"[0-9a-f]{40}", value or ""):
raise Refusal(f"{label} must be a reviewed full lowercase commit SHA")
return value
def source_for_checkout(require_wrapper):
if not require_wrapper:
return git("rev-parse", "HEAD")
parents = git("show", "-s", "--format=%P", "HEAD").split()
if len(parents) != 2:
raise Refusal("release or main CI requires a two-parent Builds wrapper")
return commit(parents[1], "canonical source parent")
def admit(source, expected_head=None, require_wrapper=False):
source = commit(source, "canonical source")
head = git("rev-parse", "HEAD")
if expected_head is not None and head != commit(expected_head, "expected_sha"):
raise Refusal("checkout HEAD differs from the reviewed dispatch head")
if git("rev-parse", f"{source}^{{commit}}") != source:
raise Refusal("reviewed canonical source is not a commit")
tree = git("rev-parse", f"{source}^{{tree}}")
if git("rev-parse", "HEAD^{tree}") != tree:
raise Refusal("Builds checkout tree differs from reviewed canonical source")
if head != source or require_wrapper:
parents = git("show", "-s", "--format=%P", "HEAD").split()
if len(parents) != 2 or parents[1] != source:
raise Refusal("Builds HEAD is neither canonical source nor its two-parent wrapper")
if git("status", "--porcelain", "--untracked-files=all"):
raise Refusal("Builds checkout contains local changes or untracked files")
return {"source_commit": source, "build_commit": head, "source_tree": tree}
def main():
if os.environ.get("GITHUB_REPOSITORY") != "Builds/smirk-backend-core":
raise Refusal("this workflow is admitted only on Builds/smirk-backend-core")
event = os.environ.get("GITHUB_EVENT_NAME")
expected = os.environ.get("EXPECTED_BUILD_SHA")
if not expected:
raise Refusal("this build requires the exact reviewed event or dispatch head")
if event not in {"push", "pull_request", "workflow_dispatch"}:
raise Refusal("this build event is not admitted")
wrapper = event != "pull_request"
canonical = source_for_checkout(wrapper)
result = admit(canonical, expected, require_wrapper=wrapper)
print(f"Admitted canonical source {result['source_commit']} at Builds {result['build_commit']}")
if __name__ == "__main__":
try:
main()
except Refusal as error:
sys.exit(f"Build source refused: {error}")
except subprocess.CalledProcessError:
sys.exit("Build source refused: Git could not resolve the reviewed source or checkout")