ci: publish to npm from a GitHub release - #497
Merged
Merged
Conversation
There was no publish workflow. Releasing meant running the build and npm publish by hand, which is how a version ships from an unverified tree, or from a tag cut somewhere other than a green master. Publishing is driven by creating a GitHub release, so cutting a version stays a deliberate act with a human behind it rather than a side effect of a push. workflow_dispatch runs the same job as a rehearsal and always passes --dry-run, so a manual run can never publish. Three things worth the lines they take: The tag is checked against package.json before anything is built. A release tagged v1.0.0 while package.json still says 0.15.0 would otherwise publish 0.15.0 under a 1.0.0 release, and npm neither allows republishing a version nor unpublishing one after 72 hours. Verified against both matching and mismatching tags, with and without the v prefix, and against a prerelease. Every check runs again on the tag, even though CI ran them on the merge commit, because a publish cannot be undone and the tag is not necessarily the commit CI saw. npm publish runs with --ignore-scripts, so what ships is exactly the tree the preceding build and check steps verified, rather than a rebuild that prepublishOnly triggers underneath npm and that nothing has looked at. It also publishes with --provenance, which needs the id-token permission granted here. Needs an NPM_TOKEN repository secret, an automation token rather than a classic one, since a classic token fails against an account requiring 2FA to publish. The release process is now documented in the README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 26, 2026
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.
There was no publish workflow — only
ci,greetingsandstale— so releasing meant running the build andnpm publishby hand. That is how a version ships from an unverified tree, or from a tag cut somewhere other than a greenmaster.Should merge before the 1.0.0 version bump, so the workflow can be rehearsed against the current
0.15.0before it is the thing standing between you and a real release.How it works
Publishing is driven by creating a GitHub release, so cutting a version stays a deliberate act with a human behind it rather than a side effect of a push.
workflow_dispatchruns the same job as a rehearsal and always passes--dry-run, so a manual run can never publish. That is how to try this before trusting it.Three things worth the lines they take
The tag is checked against
package.jsonbefore anything is built. A release taggedv1.0.0whilepackage.jsonstill says0.15.0would otherwise publish0.15.0under a1.0.0release — and npm neither allows republishing a version nor unpublishing one after 72 hours. Verified against every case:package.jsonv1.0.01.0.01.0.0(no prefix)1.0.0v1.0.00.15.0v1.0.11.0.0v1.0.0-beta.11.0.0-beta.1Every check runs again on the tag. CI already ran them on the merge commit, but a publish cannot be undone and the tag is not necessarily the commit CI saw.
npm publish --ignore-scripts. What ships is exactly the tree the precedingbuild,check-buildandcheck-packagesteps verified — not a rebuild thatprepublishOnlytriggers underneath npm and that nothing has looked at. It also publishes with--provenance, which is whyid-token: writeis granted.Setup needed before this can publish
An
NPM_TOKENrepository secret. It must be an npm automation token — a classic token fails against an account that requires 2FA for publishing.Provenance also requires the repo to be public and
repositoryinpackage.jsonto match, both of which already hold.The release process is now documented under Development → Releasing in the README.
🤖 Generated with Claude Code