ci: catch plugin manifest-version mismatches before the tag, and stop them suppressing the release channels - #93
Merged
Conversation
…n bump The publish-plugins hash guard only fires after a build, deep inside a release run and after the version tag is immutable. Add the cheap git-level proxy to ci so the fix is still a one-line manifest bump.
publish-installers and publish-repo consume only the published release, but needed the whole release workflow, so v0.16.0 published on GitHub while Homebrew, winget and the apt/yum repos silently stayed behind. Gate them on the release actually being out of draft.
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.
Fixes #92.
Why
The v0.16.0 release run failed at
release / publish-plugins:fix(links)changedsrc/plugins/gist-sync/without bumping itsmanifest.json, so the freshly built bundle no longer matched the publishedplugin-gist-sync-v1.1.0that the live marketplace catalogue pins a hash to. The guard was right to refuse — but it only runsneeds: [changelog, publish-tauri], i.e. ~20 minutes in and aftertagmadev0.16.0immutable, when the cheap fix (amend the commit) no longer exists.Worse,
publish-installersandpublish-repowereneeds: [tag, release], so that unrelated failure skipped both: Homebrew, winget and the apt/yum repos never published for v0.16.0, while the GitHub release itself published fine. Theirrelease: types: [published]triggers are dead for our own releases (GITHUB_TOKEN-authored events don't fire workflows), so there was no fallback and no retry — the only evidence was a skipped job inside a run already red for another reason.What this does
1. A cheap pre-flight check (
scripts/check-plugin-versions.mjs): for eachsrc/plugins/<id>/, fail if bundled source changed since that plugin's last published<id>-v<version>tag whilemanifest.json'sversiondid not. Test files are excluded — they never reach the bundle.This is deliberately a proxy, not a replacement: the authoritative check compares built bytes against published bytes and cannot run without a build. A source edit can be a bundler no-op, and a dependency change outside the plugin folder can alter the bytes without tripping this. The guard in
publish-plugins.ymlis untouched and remains the authority.2. It runs in two places, for different reasons. In
ci.ymlso a PR fails review rather than a release. And as aplugin-versionsjob intag-release.ymlthattagdepends on — becausetag-releasetriggers on the same push tomainand never waited forci, so a release cut by pushing straight tomaincould tag while the check was still running. Gatingtagis what actually holds the "fail before the tag exists" property.3. Distribution channels gated on the release publishing, not on every job succeeding.
release.ymlexposes apublishedoutput frompublish-release;tag-release.ymladds arelease-publishedgate that reads it, falling back to the release's own draft state on GitHub — a reusable workflow whose overall conclusion is failure is exactly the case that matters, and it must not leave the gate blank.publish-installersandpublish-reponow depend on that gate. They still do not run when the release genuinely did not publish.Verification
node scripts/check-plugin-versions.mjson this tree →all 6 plugins OK, exit 0. Confirmed it is a real pass, not a no-op: independently diffing each plugin folder against its own tag gives 0 changed files for all six.abc8158(the dead-links commit, before the 1.1.1 bump) → exit 1, namingplugin-gist-syncand bothSettingsPage.tsxandopenExternal.ts. Withtagnow depending on this job, that tag would never have been created.bash -eacross all four branches (reported true / output lost but not draft / still draft / release missing) — opens only when the release actually published, and the|| echo missingdoes not abort the step.npx tsc --noEmitclean.npx vitest run→ 261 files, 1615 tests passed.tests/pluginVersionPreflight.test.ts→ 7 passed.Not verifiable outside a real release
Whether GitHub blanks a reusable-workflow output when the called workflow's conclusion is failure. The fallback makes the gate correct either way, but the exact path it takes is only observable on the next dev→main cut.
Known limits
attach-plugin-catalogstillneeds: publish-plugins. That coupling looks correct — the catalogue describes the plugin releases — so it is left as is.src/plugins/<id>/can change bundled bytes without tripping the pre-flight. Documented in the script; the release-time hash guard remains the backstop.