"""Queue a digest-pinned, uniquely identified run on the gitops branch.""" import json import os from pathlib import Path import re import subprocess import tempfile import uuid def command(*args, cwd=None, env=None): return subprocess.run(args, cwd=cwd, env=env, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip() def resolve_release(release): if re.fullmatch(r"[0-9a-f]{40}", release): ref = release else: command("git", "check-ref-format", f"refs/tags/{release}") ref = f"refs/tags/{release}" sha = command("git", "rev-parse", "--verify", f"{ref}^{{commit}}") if not re.fullmatch(r"[0-9a-f]{40}", sha): raise ValueError("Invalid revision") # Only revisions in the trusted default branch's history may run. command("git", "merge-base", "--is-ancestor", sha, "origin/master") return sha def image_digest(reference, sha): command("docker", "pull", reference) image = json.loads(command("docker", "image", "inspect", reference))[0] if (image["Config"].get("Labels") or {}).get("org.opencontainers.image.revision") != sha: raise ValueError("Image revision label does not match the selected release") repository = reference.rsplit(":", 1)[0] matches = [item for item in image.get("RepoDigests", []) if item.startswith(repository + "@sha256:")] if len(matches) != 1 or not re.fullmatch(re.escape(repository) + r"@sha256:[0-9a-f]{64}", matches[0]): raise ValueError("No unambiguous registry digest") return matches[0] def queue(request): user, token = os.environ["GITOPS_USER"], os.environ["GITOPS_TOKEN"] if not user or not token: raise ValueError("GitOps credentials are required") url = os.environ["GITOPS_URL"] if url != "https://gitea.vandeplanque.com/tonio/car-tracker.git": raise ValueError("Unexpected GitOps repository") branch = os.environ.get("GITOPS_BRANCH", "gitops") if branch != "gitops": raise ValueError("Unexpected state branch") with tempfile.TemporaryDirectory(prefix="car-tracker-request-") as temporary: root = Path(temporary) askpass = root / "askpass.sh" askpass.write_text('#!/bin/sh\ncase "$1" in\n*Username*) printf "%s\\n" "$GITOPS_USER" ;;\n*) printf "%s\\n" "$GITOPS_TOKEN" ;;\nesac\n') askpass.chmod(0o700) env = {**os.environ, "GIT_ASKPASS": str(askpass), "GIT_TERMINAL_PROMPT": "0"} repo = root / "state" repo.mkdir() command("git", "init", cwd=repo, env=env) command("git", "remote", "add", "origin", url, cwd=repo, env=env) exists = command("git", "ls-remote", "--heads", "origin", f"refs/heads/{branch}", cwd=repo, env=env) source = branch if exists else "master" command("git", "fetch", "origin", source, cwd=repo, env=env) command("git", "checkout", "-b", branch, "FETCH_HEAD", cwd=repo, env=env) path = repo / "deploy/request.json" path.parent.mkdir(exist_ok=True) path.write_text(json.dumps(request, indent=2) + "\n") command("git", "add", "deploy/request.json", cwd=repo, env=env) command("git", "-c", "user.name=Car tracker Actions", "-c", "user.email=actions@localhost", "commit", "-m", f"Queue app run {request['request_id']} ({request['revision'][:12]})", cwd=repo, env=env) # Normal push is compare-and-swap: concurrent requests fail rather than # silently overwriting each other. Never force-push the request history. command("git", "push", "origin", f"HEAD:refs/heads/{branch}", cwd=repo, env=env) def main(): try: sha = resolve_release(os.environ["RELEASE"]) reference = f"{os.environ['REGISTRY']}/{os.environ['IMAGE_REPOSITORY']}:{sha}" digest = image_digest(reference, sha) request = {"schema_version": 1, "request_id": str(uuid.uuid4()), "revision": sha, "image": digest} queue(request) print(f"Queued request: {request['request_id']}") print(f"Image: {digest}") print("This confirms Git publication only. Inspect the production agent's outcome for collection/import status.") return 0 except (KeyError, ValueError, OSError, subprocess.SubprocessError): # Git/registry errors may contain authentication details: don't echo them. print("Request failed: check the release, published image, credentials and state branch. No run was confirmed.") return 1 if __name__ == "__main__": raise SystemExit(main())