From 9d843948a5ffe588b9b6a3a9c0a5fe49b1d6f00d Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 05:28:14 +0800 Subject: [PATCH] ci: a published version names one set of bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every package here is published as an archive of a repository at a tag, so any change to that repository changes the artefact. A release needs the version in `mcpp.toml` bumped, and nothing checked that it was: a package's own CI validates the tree in front of it and has no opinion about which numbers are already taken. Measured 2026-08-25, following openkal 0.7.0 through the ecosystem: six of the eight repositories had a branch ready to merge whose version equalled the version on `main`, which is the version already in this index. Two had gained a whole interface implementation. All were green. What follows is quiet rather than loud. `git tag 0.5.3` finds the tag present and succeeds; the archive fetched from it is the old content; both mirror legs then agree with each other and with the source archive because all three are the same old bytes. The release verifies perfectly and publishes nothing, and the only surviving trace is a second `["0.5.3"]` entry here — which Lua accepts, keeps the last of, and says nothing about. The check is textual for that reason: loading the descriptor is exactly what cannot see it. Measured against the tree — 128 descriptors, no duplicates — and against a real repeat built by inserting openkal's 0.7.0 entry twice, which it names by file, line and platform section while `loadfile` on the same file succeeds. --- .github/workflows/validate.yml | 7 +++ tests/check_duplicate_versions.lua | 78 ++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/check_duplicate_versions.lua diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 244d15d..aa28cd3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -252,6 +252,13 @@ jobs: # visible by comparing sections against each other. - name: Lint platform version parity run: lua5.4 tests/check_platform_version_parity.lua pkgs/*/*.lua + + # A published version names one set of bytes. Nothing upstream checks + # that a package changing its content also bumps its number, and a + # repeated key here is accepted by Lua and silently collapsed --- so a + # release that republished an old tag would leave no other trace. + - name: Lint duplicate versions + run: lua5.4 tests/check_duplicate_versions.lua pkgs/*/*.lua # ── Single-source-of-truth grammar check ───────────────────────── # `mcpp xpkg parse` uses EXACTLY the resolver's parser, so what # passes here is what builds for users of the pinned MCPP_VERSION. diff --git a/tests/check_duplicate_versions.lua b/tests/check_duplicate_versions.lua new file mode 100644 index 0000000..b3221c4 --- /dev/null +++ b/tests/check_duplicate_versions.lua @@ -0,0 +1,78 @@ +-- A version number, once published, names one set of bytes forever. +-- +-- WHY THIS EXISTS +-- +-- Every package in this index is published as an archive of a repository at a +-- tag, so ANY change to that repository changes the artefact. A release +-- therefore needs the version in `mcpp.toml` bumped, and nothing checks that +-- it was: a package's own CI validates the tree in front of it and has no +-- opinion about which numbers are already taken. +-- +-- Measured 2026-08-25, following openkal 0.7.0 through the ecosystem: SIX of +-- the eight repositories had a branch ready to merge whose version equalled +-- the version on `main`, which is the version already in this index. Two of +-- them had gained a whole interface implementation. Every one of those +-- packages was green. +-- +-- What happens next is quiet rather than loud. `git tag 0.5.3` finds the tag +-- present and succeeds; the archive fetched from that tag is the OLD content; +-- both mirror legs then agree with each other and with the source archive, +-- because all three are the same old bytes. The release verifies perfectly +-- and publishes nothing. The only surviving trace is a second `["0.5.3"]` +-- entry here -- and Lua accepts a repeated key in a table constructor, keeps +-- the last one, and reports nothing. +-- +-- THE RULE +-- +-- Within one platform section, a version key appears at most once. +-- +-- The check is textual and deliberately so. Loading the descriptor is exactly +-- what cannot see this: by the time the table exists, the duplicate has +-- already collapsed into the survivor. +-- +-- Usage: lua5.4 tests/check_duplicate_versions.lua [...] +-- Exits non-zero, with ::error lines naming the file, the line, and the +-- platform section the repeat is in. + +local failed = false + +-- A platform section opens with ` = {` at the indentation the +-- descriptors use inside `xpm`, and a version key is `[""]`. +-- Anything else -- `url`, `sha256`, nested tables -- is passed over. +local function check(path) + local handle = io.open(path, "r") + if not handle then + io.stderr:write(("::error file=%s::cannot be read\n"):format(path)) + failed = true + return + end + + local section, seen, line_no = nil, {}, 0 + for line in handle:lines() do + line_no = line_no + 1 + + local platform = line:match("^%s%s%s%s%s%s%s%s([%w_]+)%s*=%s*{%s*$") + if platform then + section, seen = platform, {} + end + + local version = line:match('^%s*%["([%d][%d%.]*)"%]%s*=') + if version and section then + if seen[version] then + io.stderr:write(("::error file=%s,line=%d::%s appears twice in the %s section (first at line %d). A published version names one set of bytes; bump it instead.\n") + :format(path, line_no, version, section, seen[version])) + failed = true + else + seen[version] = line_no + end + end + end + handle:close() +end + +if #arg == 0 then + io.stderr:write("usage: check_duplicate_versions.lua [...]\n") + os.exit(2) +end +for _, path in ipairs(arg) do check(path) end +os.exit(failed and 1 or 0)