Port of image_version.sh with a contract test (16 cases computed by the old script's Python). For apps with real updates, status sends a manifest that pins a digest only or a floating tag (dockge, grafana) to image_version, which takes the version from the OCI or LinuxServer label and hands it to image_tags; no usable label is STOP version_unknown. inspect exposes only the version labels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
4.0 KiB
Python
55 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Regenerates cases.json: the expected results of the OLD image_version.sh on fixed inputs.
|
|
|
|
usage: generate.py <path to old image_version.sh>
|
|
|
|
The old script runs its Python on VM103 inside a bash heredoc and asks Docker twice (`docker compose config` for the image,
|
|
`docker inspect` for the labels). This extracts that Python, swaps ONLY those two calls for values injected from the
|
|
environment, and runs it. Nothing in the old repo is modified and nothing talks to Docker.
|
|
"""
|
|
import hashlib, json, os, subprocess, sys
|
|
|
|
script_path = sys.argv[1]
|
|
source = open(script_path).read()
|
|
body = source.split("<<'PY'\n", 1)[1].rsplit("\nPY", 1)[0]
|
|
cfg_line = next(line for line in body.splitlines() if line.startswith("cfg = "))
|
|
image_line = next(line for line in body.splitlines() if line.startswith("image = "))
|
|
labels_line = next(line for line in body.splitlines() if line.startswith("raw_labels = "))
|
|
program = (body.replace(cfg_line, "cfg = {}")
|
|
.replace(image_line, "image = os.environ['IMAGE']")
|
|
.replace(labels_line, "raw_labels = os.environ['LABELS']"))
|
|
|
|
def old_result(image, labels):
|
|
env = {"IMAGE": image, "LABELS": labels if isinstance(labels, str) else json.dumps(labels), "CONTAINER": "demo-demo-1",
|
|
"SERVICE": "demo", "COMPOSE": "/dev/null", "PATH": os.environ["PATH"]}
|
|
out = subprocess.run([sys.executable, "-c", program], env=env, capture_output=True, text=True, check=True)
|
|
return json.loads(out.stdout)
|
|
|
|
D = "sha256:" + "d" * 64
|
|
LSIO = "Linuxserver.io version:- 4.0.15-ls289 Build-date:- 2026-09-01T00:00:00+00:00"
|
|
CASES = {
|
|
"digest only, OCI version label (dockge)": dict(image=f"louislam/dockge@{D}", labels={"org.opencontainers.image.version": "1.5.0", "maintainer": "x"}),
|
|
"latest with digest, OCI label (grafana)": dict(image=f"grafana/grafana:latest@{D}", labels={"org.opencontainers.image.version": "13.2.2"}),
|
|
"linuxserver build_version only": dict(image=f"linuxserver/bazarr@{D}", labels={"build_version": LSIO}),
|
|
"OCI label wins over build_version": dict(image=f"linuxserver/bazarr@{D}", labels={"org.opencontainers.image.version": "v1.5.3-ls301", "build_version": LSIO}),
|
|
"label-schema version is reported but not used": dict(image=f"org/app@{D}", labels={"org.label-schema.version": "2.1.0"}),
|
|
"empty OCI label falls back to build_version": dict(image=f"linuxserver/x@{D}", labels={"org.opencontainers.image.version": "", "build_version": LSIO}),
|
|
"build_version without the marker": dict(image=f"linuxserver/x@{D}", labels={"build_version": "custom build 7"}),
|
|
"no labels at all": dict(image=f"org/app@{D}", labels={}),
|
|
"labels are null (docker prints null)": dict(image=f"org/app@{D}", labels="null"),
|
|
"labels unreadable": dict(image=f"org/app@{D}", labels="not json"),
|
|
"no digest": dict(image="org/app:1.0", labels={"org.opencontainers.image.version": "1.0"}),
|
|
"no digest and no labels": dict(image="org/app", labels={}),
|
|
"registry with a port": dict(image=f"registry.local:5000/team/app@{D}", labels={"org.opencontainers.image.version": "3.2.1"}),
|
|
"registry with a port and a tag": dict(image=f"registry.local:5000/team/app:latest@{D}", labels={"org.opencontainers.image.version": "3.2.1"}),
|
|
"already versioned": dict(image=f"ghcr.io/linuxserver/jackett:v0.24.2601-ls30@{D}", labels={"org.opencontainers.image.version": "v0.24.2601-ls30"}),
|
|
"version label with spaces around": dict(image=f"org/app@{D}", labels={"org.opencontainers.image.version": " 1.0 "}),
|
|
}
|
|
|
|
doc = {"provenance": {"generatedBy": "fixtures/image-version/generate.py running the Python of the OLD image_version.sh",
|
|
"scriptSha256": hashlib.sha256(source.encode()).hexdigest()},
|
|
"cases": [{"name": n, "image": c["image"], "labels": c["labels"], "expected": old_result(**c)} for n, c in CASES.items()]}
|
|
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cases.json")
|
|
json.dump(doc, open(out, "w"), indent=1, ensure_ascii=False)
|
|
print("wrote", out, "cases:", len(doc["cases"]))
|