Repository navigation
feat: pin every module image by digest, and give 8 games a version list - #35
Merged
Merged
Conversation
Every module shipped a floating default image, so a pod restart could swap the game binary under a live world with no version bump and no changelog. That is not hypothetical: terraria-latest moved 1.4.4.9 -> 1.4.5.6, changed the network protocol, and broke the e2e Terraria bot. Adds the machinery to fix that and a rule to keep it fixed: - resolve_digest() returns the digest of the TAG manifest, which for a multi-arch image is the index digest covering every published platform. Deliberately NOT the per-platform digest fetch_image_config drills down to -- pinning that would cut each module to a single architecture and break the arm64 half of the e2e matrix. - Registry auth is now the standard OCI bearer challenge, so any registry resolves instead of only Docker Hub. dayz (on a self-hosted GitLab container registry) was previously skipped by every rule; it is now checked and pinnable like the rest. - _parse_ref handles digest refs. It previously left the tag glued to the repo path for repo:tag@sha256:..., which would have 404'd every request built from it, and left the registry host on the repo for non-Docker-Hub refs. Neither could surface before, because both paths were unreachable. - Rule 10 (rule_images_pinned) draws the line at defaults: ERROR on an unpinned spec.image or an unpinned `default: true` version, WARN on any other unpinned version entry. A module may legitimately offer a moving channel (factorio "Experimental", valheim "Public Test") -- picking one is the user's explicit, labelled choice. Pure string inspection, so a network blip can never downgrade it the way the image-exists rules degrade. - `validate.py --pin` rewrites templates to repo:tag@sha256:... line-wise, preserving the comments a YAML round-trip would discard, and leaves any ref it cannot resolve untouched rather than inventing a digest. Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Runs `validate.py --pin` across all 16 modules, rewriting each image ref to the readable repo:tag@sha256:... form. Every digest is the tag manifest's own digest -- a multi-arch index where upstream publishes one -- so the arm64 half of the e2e matrix keeps resolving. Two entries stay deliberately floating, marked `# gameplane:floating`: factorio's "Experimental channel" and terraria's "tModLoader latest preview". Both are image-carried pre-release channels whose purpose is to track upstream, and both are non-default, so drift is only ever the user's explicit, labelled choice. terraria's vanilla-latest default -- the exact ref whose 1.4.4.9 -> 1.4.5.6 drift broke the e2e bot -- is now pinned. Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Nine modules declared no spec.versions at all, so those games offered users no version choice whatsoever -- the Create Server wizard skipped its version step entirely (CreateServer.tsx only inserts it when spec.versions is non-empty) and Settings had nothing to switch between. Adds a pinned version list to 8 of them: 7-days-to-die, ark-survival-ascended, cs2, dayz, dont-starve-together, enshrouded, project-zomboid and v-rising. Each carries a `default: true` current entry plus older releases, and a `latest` entry marked `# gameplane:floating` so the moving tag stays available as an explicit, labelled choice. displayNames say "image release" rather than naming a game version wherever the tag is the wrapper's version rather than the game's -- following the convention palworld already set with "v2 wrapper (major line)". enshrouded is the exception: its tags really are game versions. garrys-mod is deliberately left without a list. Its only non-latest tags are base-OS variants (debian/ubuntu/-root), which are a packaging choice rather than a version, and modelling them as versions would misuse the field. Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Pinning stops upstream releases from reaching users on their own, so there has to be a deliberate path for them to reach users at all. This workflow is that path: it re-resolves every pinned tag and opens a PR with the diff. It never pushes to main and never auto-merges -- a pin refresh means the game binary changes for everyone on next restart, which is exactly the event pinning exists to put a human in front of. Monthly rather than nightly, since a PR nobody has time to review is worse than no PR. The validator runs against the NEW digests and its output is attached to the PR body, because a fresh upstream build tripping a rule is the single most useful thing a reviewer can see. Documented in the workflow: a PR opened with the default GITHUB_TOKEN does not trigger other workflows, so `validate modules` needs a nudge before merging. Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every one of the 16 modules shipped a floating default image (13 a bare
:latest), so a pod restart could swap the game binary under a live world with no version bump and no changelog. That is not hypothetical —passivelemon/terraria-docker:terraria-latestmoved from Terraria 1.4.4.9 to 1.4.5.6, changed the network protocol, and broke the e2e Terraria bot.Separately, 9 of 16 modules declared no
spec.versionsat all, so those games offered users no version choice whatsoever: the Create Server wizard skips its version step whenspec.versionsis empty, and Settings → Version had nothing to switch between.Closes the "hermetic module images" item on Gameplane's roadmap to v1.
What changed
Pinning engine (
validate.py)resolve_digest()returns the digest of the tag manifest — a multi-arch index digest where upstream publishes one. Deliberately not the per-platform digestfetch_image_configdrills down to: pinning that would cut each module to a single architecture and break the arm64 half of the e2e matrix. Verifieditzg/minecraft-server:java21pins to an OCI index coveringlinux/amd64+linux/arm64.dayz(on a self-hosted GitLab container registry) was previously skipped by every rule; it is now fully checked and pinnable, and validates clean._parse_refhandles digest refs. It previously left the tag glued to the repo path forrepo:tag@sha256:…, and left the registry host on the repo for non-Docker-Hub refs — both would have 404'd every request built from them. Neither could surface before, because both paths were unreachable.New rule 10 (
rule_images_pinned) draws the line at defaults:spec.imageor an unpinneddefault: trueversion — what a server runs when the user expressed no preference.It is pure string inspection with no registry call, so a network blip can never downgrade it the way the image-exists rules deliberately degrade.
validate.py --pinrewrites templates to the readablerepo:tag@sha256:…form, line-wise so the templates' extensive comments survive (a YAML round-trip would discard them), and leaves any ref it cannot resolve untouched rather than inventing a digest.Version lists added to 8 modules.
displayNames say "image release" rather than naming a game version wherever the tag is the wrapper's version — following the convention palworld already set with "v2 wrapper (major line)".enshroudedis the exception: its tags really are game versions.Deliberately floating, marked
# gameplane:floating: factorio's "Experimental channel" and terraria's "tModLoader latest preview" — image-carried pre-release channels whose purpose is to track upstream, both non-default, so drift is only ever the user's explicit labelled choice.garrys-moddeliberately gets no version list. Its only non-latesttags are base-OS variants (debian/ubuntu/-root), which are a packaging choice rather than a version; modelling them as versions would misuse the field.Monthly
refresh-pins.ymlre-resolves every pinned tag and opens a PR. It never pushes to main and never auto-merges — a pin refresh means the game binary changes for everyone on next restart, which is exactly the event pinning exists to put a human in front of.Verification
python3 validate.pyacross all 16 modules: 0 errors (3 clean, 13 warn-only). The remaining warnings are the pre-existingpuid-image-needs-fsgroupset plus the two intentional floating-entry warnings.--pinis idempotent: a second run reports "already pinned / no change" and respects the floating markers.