monoship ๐ฆ
April 29, 2026 ยท View on GitHub
A CLI tool and library for publishing packages from npm workspaces to registries (npmjs.org, GitHub Packages, etc.). It determines which workspace packages haven't been published yet by checking each package's version against the registry, and publishes only what's needed โ making it ideal for CI/CD pipelines alongside release-please.
When npm >= 10.0.0 is available, it shells out to npm publish directly (supporting OIDC, provenance, etc. out of the box).
Otherwise it falls back to libnpmpublish / libnpmpack.
Table of Contents
Requirements
- Node.js >= 22.0.0
- npm 7+ (workspace support required)
Installation
npm install monoship --save-dev
Usage
npx monoship \
--token <token> \
--registry <registry> \
--root <root> \
--rootPackage
Options
| Option | Type | Default | Description |
|---|---|---|---|
--token <token> | string | NODE_AUTH_TOKEN env var | Token for the registry. Optional when using OIDC trusted publishing. |
--registry <registry> | string | https://registry.npmjs.org/ | Registry URL to publish to. |
--root <root> | string | process.cwd() | Directory where the root package.json is located. |
--rootPackage | boolean | true | Also consider the root package for publishing (skipped if private: true or missing name/version). |
--tag <tag> | string | Auto-detected | Dist-tag to publish under. Overrides the prerelease identifier auto-detected from version (e.g. 1.0.0-beta.0 โ beta). Stable versions default to latest. |
Authentication
The tool supports three authentication methods, resolved in the following order:
--tokenCLI flag โ Explicit npm access token, used as-is.- OIDC Trusted Publishing โ Tokenless publishing via GitHub Actions OIDC (auto-detected when no
--tokenflag is given). Falls back toNODE_AUTH_TOKENif OIDC fails. NODE_AUTH_TOKENenvironment variable โ Default fallback.
OIDC Trusted Publishing
When running in GitHub Actions with trusted publishers configured, the tool automatically detects the OIDC environment and exchanges short-lived, per-package tokens with the npm registry โ no long-lived NPM_TOKEN secret required.
Requirements:
- npm trusted publisher configured for each package on npmjs.com
- GitHub Actions workflow with
id-token: writepermission - No
--tokenflag set (OIDC is bypassed when an explicit token is provided)
How it works:
- Detects
ACTIONS_ID_TOKEN_REQUEST_URLandACTIONS_ID_TOKEN_REQUEST_TOKENenvironment variables - Requests an OIDC identity token from GitHub with audience
npm:<registry-host> - Exchanges the identity token with the npm registry for a short-lived, package-scoped publish token
- Uses that token for publishing (each package gets its own scoped token)
If OIDC token exchange fails for a package, it falls back to NODE_AUTH_TOKEN automatically via the chain provider.
GitHub Action
monoship is also available as a GitHub Action:
- uses: tada5hi/monoship@v2
with:
token: ${{ secrets.NPM_TOKEN }}
Or with OIDC trusted publishing (no token needed):
- uses: tada5hi/monoship@v2
Action Inputs
| Input | Required | Default | Description |
|---|---|---|---|
token | No | โ | npm auth token. Optional when using OIDC trusted publishing. |
registry | No | https://registry.npmjs.org/ | Registry URL to publish to. |
root-package | No | true | Also consider the root package for publishing. |
tag | No | Auto-detected | Dist-tag to publish under. Overrides the prerelease identifier auto-detected from version. Stable versions default to latest. |
dry-run | No | false | Show what would be published without actually publishing. |
Programmatic API
import { publish } from 'monoship';
const packages = await publish({
cwd: '/path/to/monorepo',
registry: 'https://registry.npmjs.org/',
token: 'npm_...',
rootPackage: true,
dryRun: false,
});
The publish() function returns an array of Package objects for each successfully published package.
Options
| Option | Type | Default | Description |
|---|---|---|---|
cwd | string | process.cwd() | Root directory of the monorepo. |
registry | string | https://registry.npmjs.org/ | Registry URL. |
token | string | โ | Auth token (wrapped in MemoryTokenProvider internally). |
rootPackage | boolean | true | Include the root package as a publish candidate. |
tag | string | Auto-detected | Dist-tag to publish under. Overrides the prerelease identifier auto-detected from version. |
dryRun | boolean | false | Resolve dependencies and check versions without actually publishing. |
fileSystem | IFileSystem | NodeFileSystem | File system adapter. |
registryClient | IRegistryClient | HapicRegistryClient | Registry metadata adapter. |
publisher | IPackagePublisher | Auto-detected | Publisher adapter (npm CLI or libnpmpublish). |
tokenProvider | ITokenProvider | EnvTokenProvider | Token resolution adapter (overrides token). |
logger | ILogger | โ | Logger adapter. |
Custom Adapters
The library uses a hexagonal architecture โ all external I/O is behind port interfaces, making it fully testable and extensible:
import {
publish,
MemoryFileSystem,
MemoryRegistryClient,
MemoryPublisher,
MemoryTokenProvider,
NoopLogger,
} from 'monoship';
const packages = await publish({
cwd: '/project',
fileSystem: new MemoryFileSystem({ /* virtual files */ }),
registryClient: new MemoryRegistryClient({ /* virtual packuments */ }),
publisher: new MemoryPublisher(),
tokenProvider: new MemoryTokenProvider('test-token'),
logger: new NoopLogger(),
});
Available port interfaces and their adapters:
| Port | Real Adapters | Test Adapter |
|---|---|---|
IFileSystem | NodeFileSystem | MemoryFileSystem |
IRegistryClient | HapicRegistryClient | MemoryRegistryClient |
IPackagePublisher | NpmCliPublisher, NpmPublisher | MemoryPublisher |
ITokenProvider | MemoryTokenProvider, EnvTokenProvider, OidcTokenProvider, ChainTokenProvider | MemoryTokenProvider |
ILogger | ConsolaLogger | NoopLogger |
CI
GitHub Actions (with npm token)
Use with release-please โ it bumps versions and creates release PRs, then monoship handles the actual publishing:
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v4
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
if: steps.release.outputs.releases_created == 'true'
uses: actions/checkout@v4
- name: Publish
if: steps.release.outputs.releases_created == 'true'
uses: tada5hi/monoship@v2
with:
token: ${{ secrets.NPM_TOKEN }}
GitHub Actions (with OIDC Trusted Publishing)
No npm token secrets needed โ configure trusted publishers on npmjs.com for each package instead:
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v4
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
if: steps.release.outputs.releases_created == 'true'
uses: actions/checkout@v4
- name: Publish
if: steps.release.outputs.releases_created == 'true'
uses: tada5hi/monoship@v2
License
Made with ๐
Published under MIT.