ci(installer-contract): check asset existence on disk, not in YAML - #133
Merged
Conversation
The `SHA256SUMS` clause I added in v1.14.1 cannot pass. It looks for the
manifest among the `asset:` keys parsed out of release.yml, but those keys
are the build matrix and the manifest is computed *from* the matrix outputs
in a later step, so it can never be one of them. Failing it was the normal
state of a correct release workflow, and the only way to pass was to declare
a matrix entry that produced nothing — the exact defect the check existed to
catch. Measured: no repository declares that asset, podup is the gate's only
consumer and is still pinned to a tag from before the clause, so it had never
run green anywhere. Its first contact with a real repository, the pin bump in
podup#1425, failed a workflow that does produce and sign the manifest.
Extending the regex to the `gh release create` arguments would not fix it,
because that is still reading a file for evidence that a step ran. Existence
is only knowable at release time, so I check it there instead: the new
scripts/verify-release-assets.py stats the staged files after they are built,
signed and checksummed, and before `gh release create` makes the release
immutable.
What it requires is derived from the installer rather than hardcoded, by
reading the `${BASE_URL}/...` fetches out of install.sh. Hardcoding the
manifest name would have repeated the original mistake one layer down: the
old clause never looked at SHA256SUMS.sig, which install.sh also downloads
and without which it aborts, so a release missing only the signature would
have passed a gate whose stated purpose was making downloads verifiable.
The gate keeps the cross-reference check, which is sound — comparing two
declarations is what reading YAML is good for — and its error message now
says what it did and did not look at.
Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…ld not see Mutation-checked: replacing the on-disk lookup with an empty missing-set turns four of the nine red, including the missing-SHA256SUMS.sig case that the clause this script replaces had no way to detect. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Jaro-c
added a commit
to Glyndor/podup
that referenced
this pull request
Aug 17, 2026
Supersedes #1425. ## What this unblocks `#1425` bumps `installer-contract` from v1.13.0 to v1.14.1 and fails. The failure is not this repository's: v1.14.1 added a check that looks for `SHA256SUMS` among the `asset:` keys parsed out of `release.yml`. Those keys are the build matrix, and the manifest is computed *from* the matrix outputs in a later step — so it can never be one of them. Failing it is the normal state of a correct release workflow. Glyndor/.github#133 removed it and shipped v1.16.0. This bump is the first run of the amended gate against a real repository, so it is the proof the fix works. ## What replaces the check `Glyndor/.github` v1.16.0 adds `scripts/verify-release-assets.py`, which answers the same question where it is answerable: against the staged files at release time, before `gh release create` makes them immutable. #1431 wires it into `release.yml` — currently draft, waiting on this. ## Why not just update #1425 v1.14.1 is two minors behind now, and it still carries the broken clause. Merging it would reintroduce the failure. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Jaro-c
added a commit
to Glyndor/podup
that referenced
this pull request
Aug 17, 2026
Consumer side of Glyndor/.github#133. **Draft on purpose — see Blocked below.** ## Why The asset-contract gate compares `install.sh` against `release.yml` and answers *"do the two agree on the asset names"*. It cannot answer *"does the release produce them"* — a workflow file is a statement of intent, not proof that a step ran. `Glyndor/.github` carried a check that tried to answer it anyway, by looking for `SHA256SUMS` among the parsed `asset:` keys. Those keys are the build matrix, and the manifest is computed *from* the matrix outputs in a later step, so it can never match. **That is the false positive on #1425** — the gate fails this repo for doing it correctly. ## What this does Adds one step to the `release` job, between "Verify every signature against the keys consumers embed" and "Create GitHub Release". At that point everything is built, signed and checksummed, and the release is not immutable yet — so existence is a stat call rather than an inference. What it requires is derived from `install.sh`'s own `${BASE_URL}` fetches, not hardcoded. That matters: **the old check never looked at `SHA256SUMS.sig`**, which `install.sh` downloads and without which it aborts. A release missing only the signature would have passed a gate whose stated purpose was making downloads verifiable. ## Verified, and not Run locally against this repo's real `install.sh`, `install.ps1` and `release.yml`: | case | result | |---|---| | complete staging dir | `OK: all 8 files the install scripts fetch are staged` | | `SHA256SUMS.sig` removed | fails — the case the old check could not see | | `SHA256SUMS` removed | fails, naming both missing files | | the amended gate, against this repo | passes (it did not before) | **Not verified:** the workflow wiring itself. `release.yml` only runs on a tag push, and dispatching it would cut a real release, so the step order and the `upstream/` checkout path are unexercised until the next release actually runs. I am not claiming otherwise. ## Blocked The `ref:` currently pins the branch SHA of Glyndor/.github#133. That SHA becomes unreachable once #133 is squash-merged, so **this must not merge as it stands**. Order: 1. Glyndor/.github#133 merges. 2. `.github` cuts a tag. 3. #1425 bumps the `installer-contract` pin to that tag — that run is what proves the gate fix green. 4. This PR's `ref:` moves to the tag, and comes out of draft. --------- Signed-off-by: Jaro-c <75870284+Jaro-c@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.
The problem
The
SHA256SUMSclause I added in v1.14.1 cannot pass.It looks for the manifest among the
asset:keys parsed out ofrelease.yml. Those keys are the build matrix.SHA256SUMSis computed from the matrix outputs in a later step and passed togh release createas an argument, so it can never be one of them.That makes the check inverted: failing it is the normal state of a correct release workflow, and the only way to pass is to declare a matrix entry that produces nothing — the exact defect it existed to catch.
Measured on 2026-08-17:
asset: SHA256SUMS.v1.13.0, which predates the clause.Why not just fix the regex
Extending it to the
gh release createarguments still reads a file for evidence that a step ran. A workflow file is a statement of intent; it is not proof of an artifact.What I did instead
Existence is only knowable at release time, so I check it there.
scripts/verify-release-assets.pystats the staged files after they are built, signed and checksummed, and beforegh release createmakes the release immutable.What it requires is derived from the installer, by reading the
${BASE_URL}/...fetches out ofinstall.sh— not hardcoded. Hardcoding the manifest name would have repeated the original mistake one layer down: the old clause never looked atSHA256SUMS.sig, whichinstall.shalso downloads and without which it aborts, so a release missing only the signature would have passed a gate whose stated purpose was making downloads verifiable.The gate keeps its cross-reference check, which is sound — comparing two declarations is what reading YAML is good for — and its error message now says what it did and did not look at.
Verified
Against podup's real
install.sh,install.ps1andrelease.yml:OK: all 4 installer-requestable assets are published/all 2 install.ps1-requestableOK: all 8 files the install scripts fetch are stagedSHA256SUMS.sigremovedSHA256SUMSremoveddocs/reusablesregenerated withscripts/render-reusable-docs.py.Follow-up
The consumer side is a separate PR in podup, which wires the script into
release.ymlbetween "Generate checksums" and "Create GitHub Release". I will not tag a release here until that PR proves this green.