| name | publish-npm |
| description | Set up a GitHub Actions workflow to publish an npm package on release — using either a long-lived npm token or OIDC trusted publishing (no stored secret). Use when adding CI publish automation to a package repo, wiring up phucbm/publish-npm-action, switching to OIDC, or debugging publish failures in Actions. |
| when_to_use | When the user wants to auto-publish an npm package on GitHub release, set up OIDC trusted publishing, or troubleshoot npm CI publish errors. |
| allowed-tools | Read, Edit, Write, Bash |
GitHub Actions — Auto-publish npm package on Release
Canonical implementation: phucbm/publish-npm-action — a composite action that handles install → test → build → version sync → commit artifacts → publish.
Two auth methods
| Method | Secret needed | Provenance |
|---|
| npm token | NPM_TOKEN in repo secrets | No |
| OIDC trusted publishing | None | Auto (attestations) |
OIDC is preferred — no long-lived secret, short-lived job-scoped token.
Minimal workflow — npm token
name: Publish on Release
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- uses: phucbm/publish-npm-action@v1
with:
npm-token: ${{ secrets.NPM_TOKEN }}
Minimal workflow — OIDC (no token)
name: Publish on Release
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: write
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- uses: phucbm/publish-npm-action@v1
One-time npmjs.com setup for OIDC:
- Go to your package on npmjs.com → Settings → Trusted Publishers
- Click GitHub Actions, enter: org/user, repo name, workflow filename, environment (leave blank if none)
What the action does
- Setup pnpm + Node.js (configures registry-url for auth)
- Install dependencies
- Auto-detect and run tests (skips gracefully if no test script)
- Extract version from release tag (
v1.2.3 → 1.2.3)
npm version <ver> --no-git-tag-version --allow-same-version
- Run build command
- Commit
package.json + output-dir (force-added) back to main
- Publish to npm
Inputs
| Input | Default | Notes |
|---|
npm-token | `` | Leave empty to use OIDC |
node-version | 20 | |
pnpm-version | 8 | |
build-command | pnpm build | Set '' to skip build |
install-command | pnpm install --no-frozen-lockfile | |
test-command | pnpm test | |
publish-access | public | |
skip-tests | false | |
output-dir | dist/ | Force-committed even if in .gitignore |
target-branch | main | Branch artifacts are pushed to |
commit-files | `` | Extra files/patterns to commit |
Outputs
package-name, version, npm-url, tests-run
Critical gotchas (learned the hard way)
- Checkout
ref: main, not the tag. The action commits build artifacts back — checking out the tag leaves you in detached HEAD and the push fails.
fetch-depth: 0 is required. When triggered by workflow_dispatch, the action calls gh release list to find the version. Shallow clone breaks this.
- OIDC and
NODE_AUTH_TOKEN conflict. setup-node with registry-url writes an .npmrc that sets _authToken=${NODE_AUTH_TOKEN}. If NODE_AUTH_TOKEN is empty, npm falls back to OIDC — but only if you don't set the env var at all. Never pass an empty NODE_AUTH_TOKEN in the OIDC path.
- npm >= 11.5.1 required for OIDC. The action runs
npm install -g npm@latest before publishing via OIDC. GitHub-hosted runners ship with an older npm that doesn't support trusted publishing.
actions/checkout@v6 + actions/setup-node@v6. The npm OIDC guide specifies these versions; older versions don't wire OIDC correctly.
--allow-same-version prevents errors when package.json already has the release version (e.g. you pre-bumped it manually).
- Build artifacts in
.gitignore. The dist/ commit step uses git add --force — without it, ignored build output is silently skipped and the publish has stale files.
References