Skip to content

Publish from CI on a GitHub Release - #11

Merged
rahmanow merged 1 commit into
masterfrom
claude/repo-setup-features-sv89xo
Aug 28, 2026
Merged

Publish from CI on a GitHub Release#11
rahmanow merged 1 commit into
masterfrom
claude/repo-setup-features-sv89xo

Conversation

@rahmanow

Copy link
Copy Markdown
Owner

Shipping a version becomes tag-and-release, instead of someone running npm publish from a laptop.

npm version minor
git push --follow-tags

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 --provenance flag 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@latest before 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 tagged v1.2.3 publishing 1.2.3 is 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.0 and 4.0.0 pass against version 4.0.0; v5.0.0 and release-4.0.0 fail 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 prepublishOnly hook 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:

  1. Open shadowtools on npmjs.com → Settings → Trusted Publisher
  2. GitHub Actions, organization rahmanow, repository shadowtools, workflow release.yml

Until 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@v5 and actions/setup-node@v5, while ci.yml still uses @v4 — which GitHub currently warns about, since v4 targets the deprecated Node 20 runtime. I left ci.yml alone to keep this PR to one concern. Bumping it is a two-line follow-up whenever you want it.


Generated by Claude Code

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
@rahmanow
rahmanow merged commit 2e919d2 into master Aug 28, 2026
4 checks passed
This was referenced Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants