Initial public ModelForge release

This commit is contained in:
Jens
2026-09-01 21:30:16 +02:00
commit 7082ab955a
490 changed files with 104252 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
"""Prove a built Console release image talks to the API origin it was built for.
This gate exists because v1.2.0 passed every other one and still shipped a console that could not
reach its own API. Vite inlines ``VITE_API_BASE_URL`` at build time, so the defect lived in the
*artifact*, not the source: the unit tests were green, the release manifest was correct, the image
labels were correct, and the bundle inside the image pointed at ``http://localhost:8000``.
Nothing that inspects source, labels or checksums can see that. So this reads the built image.
python scripts/release_image_acceptance.py --image modelforge-web:1.2.1 \
--expect-origin http://192.0.2.10:18000
It is deliberately cheap — it extracts the served assets and the rendered nginx policy from the
image and asserts on their contents — so it can run in the release gate on any host with Docker and
without standing up a browser. A full browser pass against a disposable Compose stack remains the
acceptance step for a deployment; this is the step that stops a wrong artifact being published at
all.
Exit status is 0 when the image is coherent, 1 when it is not.
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess # noqa: S404 - fixed argv, never a shell
import sys
from dataclasses import dataclass
from urllib.parse import urlparse
#: Origins that are never correct in a published release artifact. A console built for a laptop is
#: fine; a console *published* for a laptop is the v1.2.0 defect.
DEVELOPMENT_ORIGINS = ("http://localhost:8000", "http://127.0.0.1:8000")
@dataclass
class Check:
name: str
passed: bool
detail: str
def run(*args: str) -> str:
completed = subprocess.run(
list(args), capture_output=True, text=True, encoding="utf-8", errors="replace", check=False
)
if completed.returncode != 0:
raise RuntimeError(f"{' '.join(args)} failed: {completed.stderr.strip()[:400]}")
return completed.stdout
def image_shell(image: str, script: str) -> str:
"""Run a read-only shell snippet inside the image without starting the server."""
return run("docker", "run", "--rm", "--entrypoint", "sh", image, "-c", script)
def bundle_origins(image: str) -> set[str]:
output = image_shell(
image,
"grep -ohoE 'https?://[a-zA-Z0-9._:-]+' /usr/share/nginx/html/assets/*.js 2>/dev/null "
"| sort -u",
)
return {line.strip() for line in output.splitlines() if line.strip()}
def csp_connect_src(image: str) -> str:
output = image_shell(
image,
"grep -o \"connect-src[^;]*;\" /etc/nginx/conf.d/security-headers.inc 2>/dev/null || true",
)
return output.strip()
def image_labels(image: str) -> dict[str, str]:
raw = run("docker", "image", "inspect", "--format", "{{json .Config.Labels}}", image)
return json.loads(raw or "{}") or {}
def evaluate(image: str, expected: str, version: str | None) -> list[Check]:
expected = expected.rstrip("/")
parsed = urlparse(expected)
checks: list[Check] = []
origins = bundle_origins(image)
checks.append(
Check(
"bundle targets the configured origin",
expected in origins,
f"expected {expected}; bundle references {sorted(origins) or 'none'}",
)
)
# The whole point of the gate: a development fallback must never reach a published artifact,
# unless the artifact is deliberately a development one.
leaked = sorted(o for o in DEVELOPMENT_ORIGINS if o in origins and o != expected)
checks.append(
Check(
"bundle contains no development API fallback",
not leaked,
f"development origins compiled into the bundle: {leaked}" if leaked else "none present",
)
)
connect = csp_connect_src(image)
checks.append(
Check(
"CSP connect-src names the same origin",
bool(connect) and expected in connect,
f"connect-src is {connect!r}",
)
)
directive = connect.split("connect-src", 1)[-1].split(";")[0] if connect else ""
checks.append(
Check(
"CSP connect-src is not widened",
"*" not in directive and "data:" not in directive,
f"directive is {directive.strip()!r}",
)
)
checks.append(
Check(
"CSP has no unsubstituted placeholder",
"__API_ORIGIN__" not in connect,
"placeholder still present" if "__API_ORIGIN__" in connect else "substituted",
)
)
# A console that resolves its API relative to itself would make the origin irrelevant; assert
# the compiled form actually is absolute, so the CSP coupling above is meaningful.
checks.append(
Check(
"configured origin is absolute",
parsed.scheme in {"http", "https"} and bool(parsed.netloc),
f"origin parsed as scheme={parsed.scheme!r} netloc={parsed.netloc!r}",
)
)
labels = image_labels(image)
for field in (
"org.opencontainers.image.version",
"org.opencontainers.image.revision",
"org.opencontainers.image.created",
"org.opencontainers.image.source",
"org.opencontainers.image.title",
):
value = (labels.get(field) or "").strip()
checks.append(Check(f"label {field} is populated", bool(value), value or "EMPTY"))
if version:
actual = (labels.get("org.opencontainers.image.version") or "").strip()
checks.append(
Check(
"label version matches the release",
actual == version,
f"label {actual!r} vs release {version!r}",
)
)
revision = (labels.get("org.opencontainers.image.revision") or "").strip()
checks.append(
Check(
"label revision looks like a commit sha",
bool(re.fullmatch(r"[0-9a-f]{40}", revision)),
revision or "EMPTY",
)
)
return checks
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--image", required=True, help="the built Console image to inspect")
parser.add_argument(
"--expect-origin",
required=True,
help="the API origin this image was built for, for example https://modelforge.example.com",
)
parser.add_argument("--version", default=None, help="release version the labels must declare")
parser.add_argument("--report", default=None, help="write the result as JSON")
args = parser.parse_args(argv)
print(f"Console release-image acceptance: {args.image}", flush=True)
checks = evaluate(args.image, args.expect_origin, args.version)
for check in checks:
print(f" {'OK ' if check.passed else 'FAIL'} {check.name:44} {check.detail}", flush=True)
failed = [check for check in checks if not check.passed]
if args.report:
from pathlib import Path
Path(args.report).write_text(
json.dumps(
{
"image": args.image,
"expected_origin": args.expect_origin,
"verdict": "PASS" if not failed else "FAIL",
"checks": [vars(check) for check in checks],
},
indent=2,
)
+ "\n",
encoding="utf-8",
newline="\n",
)
if failed:
print(f"\n{len(failed)} check(s) failed; this image must not be published.", file=sys.stderr)
return 1
print(f"\nAll {len(checks)} checks passed.", flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())