SQLR-12 — make release.yml publish jobs idempotent (skip-if-already-published)#156
Merged
Conversation
…ublished)
Re-dispatching release.yml at a version where some-but-not-all artifacts
already published (the v0.11.0 wave: the engine crate 413'd but every
other channel had shipped) used to fail on every job whose artifact was
already on its registry ("crate version already uploaded", "cannot
publish over existing version"). Completing a partial release meant
hand-publishing the missing crates from a local checkout.
Add a per-registry "already published?" guard to each registry-publish
job so a re-dispatch at the same version skips what's done and publishes
only what's missing:
- crates.io (publish-crate/-ask/-mcp): GET crates.io/api/v1/crates/
<name>/<version> with a User-Agent (crates.io 403s without one);
200 -> skip the cargo publish step, 404 -> publish. Transport error
falls through to publish (preserves old fail-loud behavior).
- npm (publish-nodejs/-wasm/-notes-example): `npm view <pkg>@<ver>
version` non-empty -> skip the npm publish step.
- PyPI (publish-python): GET pypi.org/pypi/sqlrite/<ver>/json logged for
visibility; flip skip-existing false->true so a re-run uploads only the
missing wheels (file-granular is the right unit for PyPI's multi-wheel
wave) instead of failing on the first already-present file.
Each guard emits a ::notice:: so the run log explains every skip. The
GitHub-Release-only jobs (publish-ffi/-desktop/-go/build-mcp-binaries)
were already idempotent via softprops/action-gh-release create-or-update
and tolerate an existing tag/release — left unchanged.
docs/release-plan.md: rewrite the "Sad path" section to document
same-version re-dispatch recovery (retiring the old "never reuse a tag,
always bump past" workaround) and correct the stale tag-all "aborts on
existing tag" claim (it skips with a ::notice::).
Verified the guard decisions against the live registries: a re-dispatch
at the published 0.11.0 skips all single-artifact channels; the genuinely
unpublished sqlrite-notes@0.11.0 still publishes; a fresh 0.99.0 publishes
everywhere. actionlint (incl. shellcheck of all run: blocks) clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
joaoh82
added a commit
that referenced
this pull request
Jun 1, 2026
…ository-field gotcha (SQLR-13) (#157) The sqlrite-notes npm trusted publisher had its Repository field set to the package's npmjs access-page URL instead of the bare repo name, so the OIDC subject claim (repo:joaoh82/rust_sqlite:environment:release) never matched the record and every publish-notes-example run failed with "OIDC token exchange error - package not found". It only surfaced once #156 made the release idempotent and a re-dispatch finally attempted the first-ever sqlrite-notes publish. - §3c: mark the trusted publisher resolved (first shipped at 0.11.0) and add a gotcha callout — set Repository to exactly `rust_sqlite`, no owner prefix, no URL. - §3b: strengthen the canonical Repository-field note to warn against pasting a URL (not just the owner-prefixed form), pointing at §3c. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merged
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.
Context
The v0.11.0 release (run 26738895572) partially failed:
sqlrite-enginehit a crates.io 413 (fixed in #154), but by then every other channel —sqlrite-ask, npm, PyPI, C FFI, Go, desktop — had already published at 0.11.0.The publish jobs were not idempotent: only
tag-allskipped existing tags. So a full re-dispatch to finish the partial release failed on every job whose artifact already published, and "Re-run failed jobs" re-checks-out the original SHA (can't pick up a fix). Completing a partial release required manualcargo publishfrom a local checkout.What this does
Adds a per-registry "already published?" guard to each registry-publish job. A re-dispatch at the same version skips what's done and publishes only what's missing.
publish-crate/publish-ask/publish-mcpGET crates.io/api/v1/crates/<name>/<ver>(mandatoryUser-Agent); 200 → skip, 404 → publish. Transport error → publish (fail-safe).publish-nodejs/publish-notes-example/publish-wasmnpm view <pkg>@<ver> versionnon-empty → skippublish-pythonGET pypi.org/pypi/sqlrite/<ver>/jsonlogged;skip-existing: false → trueso a re-run fills in only the missing wheels (file-granular — the right unit for PyPI's multi-wheel wave)Every guard emits a
::notice::so the run log explains each skip/publish.The 4 GitHub-Release-only jobs (
publish-ffi,publish-desktop,publish-go,build-mcp-binaries) were already idempotent viasoftprops/action-gh-releasecreate-or-update and tolerate an existing tag/release — unchanged.Docs:
docs/release-plan.md"Sad path" section rewritten to document same-version re-dispatch recovery (retiring the old "never reuse a tag, always bump past" workaround) and the staletag-all"aborts on existing tag" claim corrected (it skips with a::notice::).Acceptance criteria
Verification
actionlint(incl. shellcheck of everyrun:block) + YAML parse: clean. A standalone harness mirroring the exact guard shell logic, asserted against the live registries — 14/14 pass:@joaoh82/sqlrite+-wasm, PyPIsqlriteall decide skip (full re-dispatch = no-op).sqlrite-notes@0.11.0(genuinely missing from npm — a real partial-failure artifact) → decides publish.Refs: SQLR-12. Complements #154.
🤖 Generated with Claude Code