monoship ๐Ÿ“ฆ

April 29, 2026 ยท View on GitHub

npm version CI Conventional Commits

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

OptionTypeDefaultDescription
--token <token>stringNODE_AUTH_TOKEN env varToken for the registry. Optional when using OIDC trusted publishing.
--registry <registry>stringhttps://registry.npmjs.org/Registry URL to publish to.
--root <root>stringprocess.cwd()Directory where the root package.json is located.
--rootPackagebooleantrueAlso consider the root package for publishing (skipped if private: true or missing name/version).
--tag <tag>stringAuto-detectedDist-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:

  1. --token CLI flag โ€” Explicit npm access token, used as-is.
  2. OIDC Trusted Publishing โ€” Tokenless publishing via GitHub Actions OIDC (auto-detected when no --token flag is given). Falls back to NODE_AUTH_TOKEN if OIDC fails.
  3. NODE_AUTH_TOKEN environment 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: write permission
  • No --token flag set (OIDC is bypassed when an explicit token is provided)

How it works:

  1. Detects ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN environment variables
  2. Requests an OIDC identity token from GitHub with audience npm:<registry-host>
  3. Exchanges the identity token with the npm registry for a short-lived, package-scoped publish token
  4. 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

InputRequiredDefaultDescription
tokenNoโ€”npm auth token. Optional when using OIDC trusted publishing.
registryNohttps://registry.npmjs.org/Registry URL to publish to.
root-packageNotrueAlso consider the root package for publishing.
tagNoAuto-detectedDist-tag to publish under. Overrides the prerelease identifier auto-detected from version. Stable versions default to latest.
dry-runNofalseShow 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

OptionTypeDefaultDescription
cwdstringprocess.cwd()Root directory of the monorepo.
registrystringhttps://registry.npmjs.org/Registry URL.
tokenstringโ€”Auth token (wrapped in MemoryTokenProvider internally).
rootPackagebooleantrueInclude the root package as a publish candidate.
tagstringAuto-detectedDist-tag to publish under. Overrides the prerelease identifier auto-detected from version.
dryRunbooleanfalseResolve dependencies and check versions without actually publishing.
fileSystemIFileSystemNodeFileSystemFile system adapter.
registryClientIRegistryClientHapicRegistryClientRegistry metadata adapter.
publisherIPackagePublisherAuto-detectedPublisher adapter (npm CLI or libnpmpublish).
tokenProviderITokenProviderEnvTokenProviderToken resolution adapter (overrides token).
loggerILoggerโ€”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:

PortReal AdaptersTest Adapter
IFileSystemNodeFileSystemMemoryFileSystem
IRegistryClientHapicRegistryClientMemoryRegistryClient
IPackagePublisherNpmCliPublisher, NpmPublisherMemoryPublisher
ITokenProviderMemoryTokenProvider, EnvTokenProvider, OidcTokenProvider, ChainTokenProviderMemoryTokenProvider
ILoggerConsolaLoggerNoopLogger

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.