Publish packages to npm automatically in GitHub Actions whenever a change to your package's version field is detected.
This action automates a specific kind of continuous deployment to npm, where you want to publish whenever the version field in package.json changes on your main branch. If you prefer to publish on tags (for example, those created by the npm version command), or are using an alternative package manager like pnpm, you don't need this action! Simply configure setup-node with its registry-url option and call your package manager's publish command directly. This is more secure than relying on a third-party action like this one, and is more customizable.
# Publish to npm whenever a tag is pushed
name: Publish to npm
on:
push:
tags: v*
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- run: npm ci
- run: npm test
- run: npm publish --ignore-scripts
See GitHub's Node.js publishing guide and npm's trusted publishing docs for more details and examples.
🧠 Smart
Only publishes if the version number in package.json differs from the latest on npm.
🛠 Configurable Customize the version-checking behavior, the registry URL, and path of your package.
🔐 Secure
Keeps your npm authentication token secret. Doesn't read nor write to ~/.npmrc.
⚡ Fast 100% JavaScript (which is faster than Docker) and bundled to optimize loading time.
📤 Outputs Exposes the old and new version numbers, and the type of change (major, minor, patch, etc.) as variables that you can use in your workflow.
This package can be used three different ways:
🤖 A GitHub Action as part of your CI/CD process
🧩 A function that you call in your JavaScript code
🖥 A CLI that you run in your terminal
To use the GitHub Action, you'll need to add it as a step in your workflow file. By default, the only thing you need to do is set permissions.id-token to write to enable trusted publishing via OIDC.
on:
push:
branches: main
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
- run: npm ci
- run: npm test
- uses: JS-DevTools/npm-publish@v4
[!IMPORTANT] If you're publishing a private package with trusted publishing, you will still need to provide a read-only
tokenso the action can read existing versions from the registry before publish.
diff - uses: JS-DevTools/npm-publish@v4 + with: + token: ${{ secrets.NPM_TOKEN }}
You can also publish to third-party registries. For example, to publish to the GitHub Package Registry, set token to secrets.GITHUB_TOKEN and registry to https://npm.pkg.github.com:
on:
push:
branches: main
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # allow GITHUB_TOKEN to publish packages
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
- run: npm ci
- run: npm test
- uses: JS-DevTools/npm-publish@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
registry: "https://npm.pkg.github.com"
You can set any or all of the following input parameters using with:
| Name | Type | Default | Description |
|---|---|---|---|
token |
string | None | Registry authentication token, not required if using trusted publishing³ |
registry¹ |
string | https://registry.npmjs.org/ |
Registry URL to use. |
package |
string | Current working directory | Path to a package directory, a package.json, or a packed .tgz to publish. |
tag¹ |
string | latest |
Distribution tag to publish to. |
access¹ |
public, restricted |
npm defaults | Whether the package should be publicly visible or restricted. |
provenance¹ ² |
boolean | false |
Run npm publish with the --provenance flag to add provenance statements. |
strategy |
all, upgrade |
all |
Use all to publish all unique versions, upgrade for only semver upgrades. |
ignore-scripts |
boolean | true |
Run npm publish with the --ignore-scripts flag as a security precaution. |
dry-run |
boolean | false |
Run npm publish with the --dry-run flag to prevent publication. |
publishConfig in package.json.>=9.5.0.>=11.5.1 and must be run from a supported cloud provider.npm-publish exposes several output variables, which you can use in later steps of your workflow if you provide an id for the npm-publish step.
steps:
- uses: JS-DevTools/npm-publish@v4
+ id: publish
+ - if: ${{ steps.publish.outputs.type }}
+ run: echo "Version changed!"
| Name | Type | Description |
|---|---|---|
id |
string | Package identifier of the release: ${name}@${version} or empty if no release. |
type |
string | Semver release type, initial if first release, different if other change, or empty if no release. |
name |
string | Name of the package. |
version |
string | Version of the package. |
old-version |
string | Previously published version on tag or empty if no previous version on tag. |
tag |
string | Distribution tag the package was published to. |
access |
string | Access level the package was published with, or default if scoped-package defaults were used. |
registry |
string | Registry the package was published to. |
dry-run |
boolean | Whether npm publish was run in "dry run" mode. |
To use npm-package in your JavaScript code, you'll need to install it using npm or other package manager of choice:
npm install --save-dev @jsdevtools/npm-publish
You can then import it and use it in your code like this:
import { npmPublish } from "@jsdevtools/npm-publish";
// Run npm-publish with all defaults
await npmPublish({ token: "YOUR_NPM_AUTH_TOKEN_HERE" });
As shown in the example above, you should pass an options object to the npmPublish function. In TypeScript, the Options interface is available as an import.
import type { Options } from "@jsdevtools/npm-publish";
| Name | Type | Default | Description |
|---|---|---|---|
token |
string | None | Registry authentication token, not required if using trusted publishing³ |
registry¹ |
string, URL |
https://registry.npmjs.org/ |
Registry URL to use. |
package |
string | Current working directory | Path to a package directory, a package.json, or a packed .tgz to publish. |
tag¹ |
string | latest |
Distribution tag to publish to. |
access¹ |
public, restricted |
npm defaults | Whether the package should be publicly visible or restricted. |
provenance¹ ² |
boolean | false |
Run npm publish with the --provenance flag to add provenance statements. |
strategy |
all, upgrade |
all |
Use all to publish all unique versions, upgrade for only semver upgrades. |
ignoreScripts |
boolean | true |
Run npm publish with the --ignore-scripts flag as a security precaution. |
dryRun |
boolean | false |
Run npm publish with the --dry-run flag to prevent publication. |
logger |
object | None | Logging interface with debug, info, and error log methods. |
temporaryDirectory |
string | os.tmpdir() |
Temporary directory to hold a generated .npmrc file |
publishConfig in package.json.>=9.5.0.>=11.5.1 and must be run from a supported cloud provider.The npmPublish() function returns a promise of a Results object. In TypeScript, the Results interface is available as an import.
import type { Results } from "@jsdevtools/npm-publish";
| Name | Type | Description |
|---|---|---|
id |
Optional string | Package identifier of the release: ${name}@${version} or undefined if no release. |
type |
Optional string | Semver release type, initial if first release, different if other change, or undefined if no release. |
name |
string | Name of the package. |
version |
string | Version of the package. |
oldVersion |
Optional string | Previously published version on tag or undefined if no previous version. |
tag |
string | Distribution tag that the package was published to. |
access |
Optio |
$ claude mcp add npm-publish \
-- python -m otcore.mcp_server <graph>