mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-21 14:20:06 +00:00
Replace nine identical image build pairs with one same-run artifact producer. Admit the producer's manifest, checkout, run, attempt, archive and loaded image identities before starting each isolated stack. Keep fixed registry pins, local seed defaults, every journey gate and existing browser budgets. Provider refusals remain terminal without mirrors, login, pull retries or rebuild fallbacks. Record the contract and narrowly scoped regression proof. Change-source: pulse-maintainer
174 lines
7.8 KiB
Python
174 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Build once, then admit the same run/source-bound E2E images in every job.
|
|
|
|
This is a same-run artifact, not a registry mirror or a cross-run image cache.
|
|
Docker failures are terminal: no login, alternate registry, pull retry or rebuild
|
|
fallback is performed. Only the existing secret-free E2E stack is distributed.
|
|
"""
|
|
|
|
import argparse
|
|
import gzip
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
COMPOSE = ROOT / "tests/integration/docker-compose.test.yml"
|
|
ARCHIVE = "images.tar.gz"
|
|
MANIFEST = "manifest.json"
|
|
IMAGES = ("pulse:test", "pulse-mock-github:test", "pulse-e2e-seed:test")
|
|
REVISION_LABEL = "org.opencontainers.image.revision"
|
|
SHA256 = r"[0-9a-f]{64}"
|
|
MAX_ARCHIVE_BYTES = 1024 * 1024 * 1024
|
|
|
|
|
|
def run(*args, capture=False):
|
|
return subprocess.run(
|
|
args, cwd=ROOT, check=True, text=True,
|
|
stdout=subprocess.PIPE if capture else None,
|
|
).stdout
|
|
|
|
|
|
def digest(path):
|
|
with path.open("rb") as stream:
|
|
return hashlib.file_digest(stream, "sha256").hexdigest()
|
|
|
|
|
|
def seed_image():
|
|
# Follow the committed compose pin, never an environment/registry override.
|
|
block = COMPOSE.read_text().split(" seed-bootstrap-token:\n", 1)[1].split("\n mock-github:", 1)[0]
|
|
match = re.search(
|
|
r"(?m)^ image: \$\{PULSE_E2E_SEED_IMAGE:-(alpine:[0-9.]+@sha256:" + SHA256 + r")\}$",
|
|
block,
|
|
)
|
|
if not match:
|
|
raise ValueError("E2E seed image must retain its exact committed Alpine pin")
|
|
return match[1]
|
|
|
|
|
|
def binding():
|
|
source = os.environ["GITHUB_SHA"]
|
|
run_id = os.environ["GITHUB_RUN_ID"]
|
|
attempt = os.environ["GITHUB_RUN_ATTEMPT"]
|
|
if not re.fullmatch(r"[0-9a-f]{40}", source):
|
|
raise ValueError("Invalid E2E checkout identity")
|
|
if any(not re.fullmatch(r"[1-9][0-9]*", value) for value in (run_id, attempt)):
|
|
raise ValueError("Invalid E2E run identity")
|
|
if run("git", "rev-parse", "HEAD", capture=True).strip() != source:
|
|
raise ValueError("E2E checkout does not match this workflow source")
|
|
return {"source_sha": source, "run_id": run_id, "run_attempt": attempt}
|
|
|
|
|
|
def inspect_images(source):
|
|
ids = {}
|
|
for image in IMAGES:
|
|
record = json.loads(run("docker", "image", "inspect", "--format", "{{json .}}", image, capture=True))
|
|
if not re.fullmatch("sha256:" + SHA256, record["Id"]):
|
|
raise ValueError("Invalid E2E image identity")
|
|
if record["Os"] != "linux" or record["Architecture"] != "amd64":
|
|
raise ValueError("E2E images must target Linux amd64")
|
|
if image != "pulse-e2e-seed:test":
|
|
labels = record["Config"].get("Labels") or {}
|
|
if labels.get(REVISION_LABEL) != source:
|
|
raise ValueError("E2E image revision does not match the checkout")
|
|
ids[image] = record["Id"]
|
|
return ids
|
|
|
|
|
|
def build(directory, identity):
|
|
directory.mkdir(parents=True, exist_ok=False)
|
|
label = f"{REVISION_LABEL}={identity['source_sha']}"
|
|
# Keep the e2e_runtime target and empty release tags: both mock and
|
|
# non-mock registration jobs already used this exact build contract.
|
|
run("docker", "build", "--platform=linux/amd64", "--label", label,
|
|
"-t", IMAGES[0], "--target", "e2e_runtime", "--build-arg", "GO_BUILD_TAGS=", ".")
|
|
run("docker", "build", "--platform=linux/amd64", "--label", label,
|
|
"-t", IMAGES[1], "./tests/integration/mock-github-server")
|
|
pinned_seed = seed_image()
|
|
run("docker", "image", "pull", "--platform=linux/amd64", pinned_seed)
|
|
run("docker", "image", "tag", pinned_seed, IMAGES[2])
|
|
ids = inspect_images(identity["source_sha"])
|
|
raw = directory / "images.tar"
|
|
run("docker", "image", "save", "--output", str(raw), *IMAGES)
|
|
archive = directory / ARCHIVE
|
|
with raw.open("rb") as incoming, archive.open("xb") as outgoing:
|
|
with gzip.GzipFile(filename="", fileobj=outgoing, mode="wb", compresslevel=1, mtime=0) as compressed:
|
|
shutil.copyfileobj(incoming, compressed, length=1024 * 1024)
|
|
raw.unlink()
|
|
size = archive.stat().st_size
|
|
if not 0 < size <= MAX_ARCHIVE_BYTES:
|
|
raise ValueError("E2E image bundle exceeds the transfer budget")
|
|
manifest = {
|
|
"schema_version": 1, **identity, "seed_image": pinned_seed, "images": ids,
|
|
"archive_sha256": digest(archive), "archive_bytes": size,
|
|
}
|
|
path = directory / MANIFEST
|
|
with path.open("x") as stream:
|
|
json.dump(manifest, stream, sort_keys=True)
|
|
stream.write("\n")
|
|
# The job output is the trust anchor, not a checksum supplied beside bytes.
|
|
with Path(os.environ["GITHUB_OUTPUT"]).open("a") as stream:
|
|
stream.write(f"manifest-sha256={digest(path)}\n")
|
|
print("Built one source-bound E2E bundle (two builds and the pinned seed image)")
|
|
|
|
|
|
def load(directory, identity):
|
|
expected = os.environ["PULSE_E2E_MANIFEST_SHA256"]
|
|
if not re.fullmatch(SHA256, expected):
|
|
raise ValueError("Missing or invalid producer manifest identity")
|
|
manifest_path = directory / MANIFEST
|
|
archive = directory / ARCHIVE
|
|
for path in (manifest_path, archive):
|
|
if path.is_symlink() or not path.is_file():
|
|
raise ValueError("E2E bundle files must be regular files")
|
|
if manifest_path.stat().st_size > 16384 or digest(manifest_path) != expected:
|
|
raise ValueError("E2E manifest differs from the producer job output")
|
|
manifest = json.loads(manifest_path.read_text())
|
|
keys = {"schema_version", *identity, "seed_image", "images", "archive_sha256", "archive_bytes"}
|
|
if not isinstance(manifest, dict) or set(manifest) != keys or type(manifest["schema_version"]) is not int or manifest["schema_version"] != 1:
|
|
raise ValueError("Unsupported E2E manifest")
|
|
if any(manifest[key] != value for key, value in identity.items()):
|
|
raise ValueError("E2E bundle belongs to another source, run or attempt")
|
|
if manifest["seed_image"] != seed_image():
|
|
raise ValueError("E2E seed image differs from the committed compose pin")
|
|
ids = manifest["images"]
|
|
if not isinstance(ids, dict) or set(ids) != set(IMAGES):
|
|
raise ValueError("E2E bundle must contain the three fixed image identities")
|
|
if any(not isinstance(value, str) or not re.fullmatch("sha256:" + SHA256, value) for value in ids.values()):
|
|
raise ValueError("Invalid producer E2E image identities")
|
|
size = archive.stat().st_size
|
|
if type(manifest["archive_bytes"]) is not int or not 0 < size <= MAX_ARCHIVE_BYTES or size != manifest["archive_bytes"]:
|
|
raise ValueError("E2E image archive size does not match the producer")
|
|
if not isinstance(manifest["archive_sha256"], str) or not re.fullmatch(SHA256, manifest["archive_sha256"]):
|
|
raise ValueError("Invalid producer archive identity")
|
|
if digest(archive) != manifest["archive_sha256"]:
|
|
raise ValueError("E2E image archive differs from the producer")
|
|
# No load, start, pull or build occurs before all admission checks pass.
|
|
run("docker", "image", "load", "--input", str(archive))
|
|
if inspect_images(identity["source_sha"]) != ids:
|
|
raise ValueError("Loaded E2E image identities differ from the producer")
|
|
print("Admitted the producer's exact E2E images for this source/run/attempt")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("operation", choices=("build", "load"))
|
|
parser.add_argument("directory", type=Path)
|
|
args = parser.parse_args()
|
|
try:
|
|
identity = binding()
|
|
(build if args.operation == "build" else load)(args.directory, identity)
|
|
except (ValueError, KeyError, IndexError, OSError, subprocess.CalledProcessError) as error:
|
|
print(f"E2E image admission failed: {error}", file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|