Refuse a tag that is not on main or whose CI did not pass - #497
Merged
Conversation
`publish` depended on `version-consistency` and nothing else, so pushing a tag created the GitHub Release and the six checks that decide whether it deserved to exist ran afterwards, if the operator remembered. That is how 0.7.0 was published with its headline feature broken: the checks were a written procedure, and a procedure is not a dependency. Section 4 is now the `install-gate` job and `publish` needs it. All six rows are automated and every one of them is blocking; none was dropped or made advisory, because a gate claiming six checks while running four is worse than one honestly running five. Two rows needed real fixtures rather than approximations. The PATH-less commit runs under `env -i` so node is genuinely absent, which is what makes a rejection evidence that the stub used its recorded interpreter instead of an ambient one. The stale-hook row installs a healthy stub first and then replaces only its body, so the fixture is a stale stub rather than a missing one. That second fixture also produces a hook-runtime fail of its own, and doctor exits 1 on it. The row being qualified is `commit-msg-hook`, whose required verdict is `warn`, so the check reads that row out of the JSON rather than the process exit -- reading the exit here would have qualified the wrong thing. The clone is of the pushed tag from the remote and runs the shipped bundle with no build and no install, because building in this job would qualify an artefact the tag does not carry. Limit: the job runs on ubuntu-latest only, so it qualifies the POSIX install; the Windows path is still covered by `install-ps1` in CI against the branch rather than against the tag Ruled-out: reusing the workflow's checkout instead of cloning | the row asks whether a fresh clone works, and a checkout is not the thing users get Ruled-out: making the stale-hook row non-blocking because its fixture also fails another check | the confounding finding is separable by reading the row, and a row excused for being awkward is the fail-open being repaired Warn: `publish` now waits on a job that clones over the network, so a transient clone failure withholds a release for a tag that is already pushed -- the tag is the durable thing and re-running the job is the recovery Blast: system Undo: easy Certainty: firm Verified: the six rows run end to end against a fresh clone and exit 0; the workflow loads as YAML with publish declaring both dependencies; and the gate is not vacuous -- breaking the doctor row exits 1, and comparing the plugin version against a wrong expectation exits 1 Provenance: authored Record-Id: r-failclosed493
The section-4 job made the installation a prerequisite, and a review found the gap that left: the workflow accepts every `v*` tag. Nothing asked whether the tagged commit is on the release branch, and nothing asked whether CI passed at that exact commit. Tagging any dev commit published it, and tagging a commit whose CI failed published that. Two scripts rather than two `if:` expressions, because a condition in YAML cannot be tested and these are the conditions that decide whether a wrong artefact reaches users. The CI gate asks whether the required checks passed, not whether any failure is visible. Everything that is not a success refuses: failure, cancelled, timed out, skipped, queued, still running, and a required check absent from the response entirely. An empty result set is a refusal rather than a quiet pass -- that inversion is the defect this repository has now published twice, and it is the reason the required list is declared in the script instead of inferred from whatever happens to be present at the commit. The ancestry gate refuses when it cannot see enough history to answer, so a shallow checkout produces a refusal rather than an accident. The job checks out with full depth for the same reason. `publish` now waits on all four, and has no `if:`. An omitted dependency is itself a bypass: nothing can withhold a release for a job it was never asked to wait for, so the test asserts the complete set rather than the absence of an escape hatch alone. Limit: the gates qualify the commit and its CI, not the tag's authorship or timing -- anyone who can push a `v*` tag to a qualified commit on main can still publish, and expiring or re-pointing a tag after these jobs pass is outside what any of them observe Ruled-out: expressing either check as an `if:` on publish | a condition in YAML has no test, and a release gate whose logic cannot fail in a suite is a claim rather than a control Ruled-out: treating a missing required check as nothing to report | that is the empty-set inversion above, and it is exactly how a release with no CI at all would have published Warn: the required-check list is a literal, so a job renamed in the CI workflow becomes a check this gate waits for and never finds -- it fails closed, which is right, and the failure will read as a CI problem rather than a rename Blast: system Undo: easy Certainty: firm Verified: fifteen cases pass, and the two scripts were driven directly -- a tag on main accepts, a tag on a side branch refuses, a shallow clone refuses rather than passing, six successes accept, and failure, cancelled, timed out, skipped, in-progress, an absent required check, an empty payload and successes recorded for a different commit each refuse Provenance: authored Record-Id: r-pubprereq
CommitLore — record lintTrailers: clean — 2 commits in Active constraints for the paths this PR touchesLimits (4)
Ruled out (12)
Warnings (6)
Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR. |
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.
Correction lane for the v0.7.1 promotion FAIL. Stacked on #495.
The section-4 job (#495) made the installation a prerequisite. The review found the gap that left: the workflow accepts every
v*tag. Nothing asked whether the tagged commit is on the release branch, and nothing asked whether CI passed at that exact commit. Tagging any dev commit published it; tagging a commit whose CI failed published that.Two scripts rather than two
if:expressions, because a condition in YAML cannot be tested and these are the conditions that decide whether a wrong artefact reaches users.The CI gate asks whether the required checks passed, not whether any failure is visible. Everything that is not a success refuses — failure, cancelled, timed out, skipped, queued, still running, and a required check absent from the response entirely. An empty result set is a refusal rather than a quiet pass; that inversion is the defect this repository has now published twice.
The ancestry gate refuses when it cannot see enough history to answer, so a shallow checkout produces a refusal rather than an accident.
publishwaits on all four jobs and has noif:. An omitted dependency is itself a bypass — nothing can withhold a release for a job it was never asked to wait for — so the test asserts the complete set, not just the absence of an escape hatch.Evidence
Both scripts were driven directly, not only through the suite:
main/ tag on a side branch15 cases pass. The six required check names were also compared against a real CI run on
main: exact match, so the gate cannot deadlock the release it guards.