Repository navigation
Check that no caller can leave a release sitting as a draft - #23
Open
imanimanyara wants to merge 2 commits into
Open
imanimanyara wants to merge 2 commits into
imanimanyara wants to merge 2 commits into
Conversation
Deleting a tag does not delete its GitHub release -- it reverts it to a draft. The release keeps its body and assets and stops being visible, and the state outlives the tag: laranail/validation still holds v1.0.0 and v1.0.1 as drafts from tags withdrawn on 2026-08-24, neither of which exists on the remote any more. `gh release edit` leaves that flag exactly as it finds it, so an edit after a tag is deleted and re-pushed republishes nothing on a run that still goes green: correct tag, invisible release. Nothing else reports it -- the tag resolves, composer installs, and the tag-currency job next door passes, because none of them reads the release. It was found by reading one, in laranail/validation-js, whose release had to be published by hand. The check is laranail/package-tools' scripts/verify-release-publish.py and it follows verify-action-pins.py exactly, including the path argument that makes it point at the caller rather than at package-tools. It needs no token, because it is a text scan. Most callers pass it by finding nothing: they use softprops/action-gh-release, which publishes an existing draft on its own. That is why the script carries its own matcher controls, and why the header comment now says so -- a check whose clean answer is an empty result can lie by simply stopping matching.
imanimanyara
marked this pull request as ready for review
November 8, 2026 05:11
`pull_request` with no `types` defaults to opened, synchronize and reopened, and the job skips a draft. So a pull request opened as a draft never gets this check: marking it ready re-triggers nothing, the skipped run stands, and the pull request can merge with its only job never having run. Measured on this very pull request, which is a draft deliberately -- it must not merge before laranail/package-tools#93. Marking it ready left `audit script tests` showing "skipping" from the draft run. Adding `ready_for_review` is additive: every event that triggered the workflow before still does.
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.
Summary
Deleting a tag does not delete its GitHub release — it reverts it to a draft. The release keeps its body and its assets and simply stops being visible, and the state outlives the tag:
laranail/validationstill holdsv1.0.0andv1.0.1as drafts from tags withdrawn on 2026-08-24, neither of which exists on the remote any more.gh release editleaves that flag exactly as it finds it, so an edit after a tag is deleted and re-pushed — how this family moves a pre-stable tag wherevergit push --forceis unavailable — republishes nothing on a run that still goes green: correct tag, invisible release.Nothing else reports that state. The tag resolves,
composerinstalls, and thetag-currencyjob next door passes, because none of them reads the release. It was found by reading one, inlaranail/validation-js, whose release had to be published by hand.Measured 2026-11-08 across the 286 workflow files in
laranail/packages: four hand-rolled call sites in three repositories, all with green runs behind them. Each is fixed in its own pull request; this is the job that stops the next one.What this adds
One input (
run-release-publish, default true) and one job,release-publish, placed afteraction-pinsbecause it is the other no-network, caller-path check. It mirrors it line for line, including the path argument —action-pins' own comment records that deriving the target from__file__silently scanned the wrong repository once the script stopped being a local copy. Unlike the other three jobs it passes no token, because it is a text scan.The header comment gains a paragraph on why this check is the odd one out: most callers pass it by finding nothing, since they create and update their release with
softprops/action-gh-release, which publishes an existing draft on its own ("omit it to publish after upload", in its own input description). A check whose clean answer is an empty result can lie by simply stopping matching, which is why that script drives its own matcher against built-in controls on every invocation and refuses to report anything when one misbehaves.What each of the 18 callers will see
Measured against the local checkouts on 2026-11-08:
ai-compliance artisan-ui barua captcha chrono confetti console db-console db-console-webui db-tools emojis package-management package-scaffolder pdf-toolkit polyglot sis-wrapper toolkit.validationfails until validation#42 merges. It is a caller and holds two of the four call sites, so expect that one red hygiene run, or merge validation#42 first. Verified by running the script against itsmain: exit 1, bothrelease.ymlandrelease-benchmark.yml.password-history,password-toolsandvalidation-jsare not hygiene callers, which is why each also carries its own Pest guard. Those stay — they cover repositories this workflow does not reach.Verification
symfony/yaml, it has the 6 inputs and 4 jobs, and the new job's three steps with noenv.verify-release-publish.pyrun against this repository:8 workflow files, no hand-rolled release edit, exit 0.laranail/validation@main: exit 1, 2 findings — so the job will do something, rather than passing everything.shellcheck scripts/*.sh scripts/tests/*.shclean, andscripts/tests/*.test.sh13 passed / 0 failed. They are whataudit-tests.ymlruns, and it skips draft pull requests (if: github.event.pull_request.draft != true), so this draft shows no checks by design — marking it ready runs that one job.vcs-orgs-presence/bin/health-audit.sh --offline: 0 failures, 18 warnings, all pre-existing (missing client checkouts, and the live-API checks--offlineskips). It reads no workflow files at all —grep -cE 'workflows|hygiene'over it returns 0 — so it confirms the tree is unchanged rather than confirming anything about this diff.Noted, not fixed
The README's "Reusable workflows" list names
tests.yml,static-analysis.yml,security.ymlandrelease.yml, and omitshygiene.yml,keepalive.ymlandaudit-tests.yml. Pre-existing and a separate concern.