#!/usr/bin/env python3 """Emit the complete, self-contained deployment for this rig. python3 ctrl/manifest.py [namespace] > generated/.yaml The output is the ARTIFACT. It carries everything — namespace, the vite app and bundle.json embedded in a ConfigMap, the Pod and the Service — so it applies unmodified to any cluster: kubectl apply -f generated/sample-rig.yaml On kind, MetalLB answers the `type: LoadBalancer` Service. On a real external cluster the cloud load balancer does. Same file, no edits, no branch — which is the point: what runs locally is byte-identical to the deployment applied elsewhere, so local success actually means something. `ctrl/bundle.sh up` applies this same generated output rather than a separate code path, so the local convenience wrapper can never drift from the artifact. Stdlib only, deliberately: this must run on a bare staging workstation before anything is installed, so it cannot depend on PyYAML or a template engine. Open seam — not built: everything here derives from bundle.json plus a target namespace. A Pulumi or Terraform emitter would sit beside this file consuming the same inputs; nothing above it assumes the artifact is YAML. """ import json import re import sys import zlib from pathlib import Path ROOT = Path(__file__).resolve().parent.parent UI = ROOT / "rig-ui" # Files embedded into the ConfigMap, mounted read-only at /src in the pod and # copied into vite's layout at start (see rig-ui/k8s.yaml). Flat on purpose: # ConfigMap keys cannot contain '/'. EMBEDDED = { "bundle.json": ROOT / "bundle.json", "package.json": UI / "package.json", "vite.config.js": UI / "vite.config.js", "index.html": UI / "index.html", "main.js": UI / "src" / "main.js", "style.css": UI / "src" / "style.css", } def slug(name: str) -> str: """Reduce a folder name to a DNS label, matching ctrl/bundle.sh's rule.""" out = re.sub(r"[^a-z0-9-]", "-", name.lower()).strip("-") return out or "rig-bundle" def block(text: str, indent: int) -> str: """Indent a file's contents for a YAML literal block scalar. Blank lines are emitted truly empty rather than as whitespace: trailing spaces on an otherwise blank line are legal YAML but show up as diff noise in a committed artifact. """ pad = " " * indent return "\n".join(pad + line if line.strip() else "" for line in text.splitlines()) def checksum(parts: list[str]) -> str: """Stable content hash of everything embedded, stamped as a label. A mounted ConfigMap updates in place without restarting anything, so without a visible change nothing signals that the pod is serving stale content. """ return str(zlib.crc32("".join(parts).encode()) & 0xFFFFFFFF) def build(namespace: str) -> str: contents = {} for key, path in EMBEDDED.items(): if not path.exists(): sys.exit(f"missing input: {path}") contents[key] = path.read_text() # Fail loudly here rather than shipping an artifact that renders an error. try: json.loads(contents["bundle.json"]) except json.JSONDecodeError as exc: sys.exit(f"bundle.json is not valid JSON: {exc}") app = (UI / "k8s.yaml").read_text() app = app.replace("__RIG_NAME__", namespace) data = "\n".join( f" {key}: |\n{block(text, 4)}" for key, text in sorted(contents.items()) ) return f"""# GENERATED by ctrl/manifest.py — do not edit. # Regenerate with: make manifest # # Self-contained: applies as-is to any cluster, local kind or external. # kubectl apply -f this-file.yaml # # Namespace carries the identity, so several rigs coexist in one cluster. apiVersion: v1 kind: Namespace metadata: name: {namespace} labels: rig.bundle/name: {namespace} --- apiVersion: v1 kind: ConfigMap metadata: name: rig-ui namespace: {namespace} labels: rig.bundle/checksum: "{checksum(list(contents.values()))}" data: {data} --- {_namespaced(app, namespace)} """ def _namespaced(doc: str, namespace: str) -> str: """Add `namespace:` to each resource so the artifact applies without -n. rig-ui/k8s.yaml omits it on purpose — applied by hand it should land in whatever namespace you choose. Pinning it belongs to the generated artifact, which has to be self-contained. """ return re.sub( r"^(metadata:\n(?:[ \t]+.*\n)*?)([ \t]+)(name: rig-ui)$", lambda m: f"{m.group(1)}{m.group(2)}{m.group(3)}\n{m.group(2)}namespace: {namespace}", doc.strip(), flags=re.MULTILINE, ) if __name__ == "__main__": target = sys.argv[1] if len(sys.argv) > 1 else slug(ROOT.name) sys.stdout.write(build(target))