Skip to content

tada5hi/monoship

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

monoship 📦

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

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).

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

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.
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.
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.

About

Publish npm workspace packages to registries. Supports OIDC trusted publishing, workspace: protocol resolution, and version conflict detection.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors