Publish from CI on a GitHub Release - #11
Merged
Merged
Conversation
Shipping a version becomes tag-and-release instead of someone running npm publish from a laptop. Authentication uses npm trusted publishing over OIDC, so no long-lived token exists — not in repository secrets, not on a machine. It needs id-token: write and npm 11.5.1 or newer, which is why the workflow upgrades npm before publishing rather than trusting the runner's bundled version. Provenance is attested automatically. The workflow refuses to publish when the release tag disagrees with package.json. That check costs seconds and cannot be done after the fact, because a published npm version can never be replaced. Tests run through the existing prepublishOnly hook, so a failing suite stops the publish too. Trusted publishing also requires a one-time Trusted Publisher entry on npmjs.com; until that exists the publish step fails with an authentication error, which is the safe direction. The README documents both the release steps and that setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJR2DDBimsijgYgZS3bUS8
This was referenced Aug 28, 2026
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.
Shipping a version becomes tag-and-release, instead of someone running
npm publishfrom a laptop.Then publish a GitHub Release for the tag. The workflow installs, checks the tag, runs the tests, and publishes.
No token, anywhere
Authentication uses npm trusted publishing over OIDC: GitHub Actions proves its identity to the registry directly. There is no long-lived token — not in repository secrets, not on anyone's machine — and npm attests the package's provenance automatically, so no
--provenanceflag is needed.That drove two details in the workflow:
id-token: write, scoped to the publish job. Without it the registry has nothing to verify.npm install -g npm@latestbefore publishing. Trusted publishing needs npm 11.5.1 or newer, which is more recent than the npm bundled with some Node versions. The step prints the version so a regression is visible in the log rather than silent.Two guards before anything reaches the registry
Tag must match
package.json. A release taggedv1.2.3publishing1.2.3is the only accepted combination. This matters more than a usual check because a published npm version can never be replaced — catching a mismatch afterwards is not an option. I tested the logic both ways:v4.0.0and4.0.0pass against version4.0.0;v5.0.0andrelease-4.0.0fail with a non-zero exit.The tag is passed through an environment variable rather than interpolated into the shell, so a crafted tag name cannot inject commands.
Tests must pass. They run through the existing
prepublishOnlyhook from #10, so a failing suite stops the publish without the workflow needing its own test step.One-time setup needed before this works
Trusted publishing has to be enabled on the registry side, which only a package maintainer can do:
shadowtoolson npmjs.com → Settings → Trusted Publisherrahmanow, repositoryshadowtools, workflowrelease.ymlUntil that exists, the publish step fails with an authentication error — the safe direction to fail in. The README's new Releasing section documents both this and the release steps.
What I could not verify
I tested the YAML parses, the tag guard's logic, and that the workflow file is excluded from the published tarball. I could not test the OIDC handshake itself — that only exercises on a real release, against a registry configured with the Trusted Publisher entry above. If the first release fails on authentication, that is where to look.
Also worth knowing: npm's own documentation is blocked by this environment's network policy, so the specifics above come from the GitHub changelog announcement and community write-ups rather than from npmjs.com directly. Worth a glance at the docs when you configure it.
A deliberate inconsistency
This workflow pins
actions/checkout@v5andactions/setup-node@v5, whileci.ymlstill uses@v4— which GitHub currently warns about, since v4 targets the deprecated Node 20 runtime. I leftci.ymlalone to keep this PR to one concern. Bumping it is a two-line follow-up whenever you want it.Generated by Claude Code