Skip to content

ci: publish to npm from a GitHub release - #497

Merged
fjmorant merged 1 commit into
masterfrom
ci/publish-workflow
Sep 26, 2026
Merged

fjmorant merged 1 commit into
masterfrom
ci/publish-workflow

Conversation

@fjmorant

Copy link
Copy Markdown
Owner

There was no publish workflow — only ci, greetings and stale — so releasing meant running the build and npm publish by hand. That is how a version ships from an unverified tree, or from a tag cut somewhere other than a green master.

Should merge before the 1.0.0 version bump, so the workflow can be rehearsed against the current 0.15.0 before 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.

gh release create v1.0.0 --title 1.0.0 --notes-from-tag

workflow_dispatch runs 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.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 every case:

tag package.json
v1.0.0 1.0.0 publishes
1.0.0 (no prefix) 1.0.0 publishes
v1.0.0 0.15.0 refused — the mistake this exists for
v1.0.1 1.0.0 refused
v1.0.0-beta.1 1.0.0-beta.1 publishes

Every 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 preceding build, check-build and check-package steps verified — not a rebuild that prepublishOnly triggers underneath npm and that nothing has looked at. It also publishes with --provenance, which is why id-token: write is granted.

Setup needed before this can publish

An NPM_TOKEN repository 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 repository in package.json to match, both of which already hold.

The release process is now documented under Development → Releasing in the README.

🤖 Generated with Claude Code

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>
@fjmorant fjmorant added this to the 1.0.0 — API freeze milestone Sep 26, 2026
@fjmorant
fjmorant merged commit bf93a72 into master Sep 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant