Skip to content

lacymorrow/shipx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shipx

Interactive release CLI ➔ bump, tag, publish, and ship — npm · Cargo · Homebrew · GitHub.

npm version npm downloads CI License Node

shipx running an interactive release in the terminal

Note

shipx is a thin, opinionated wrapper around npm publish, git tag, gh release, cargo set-version, and a Homebrew formula update. It's a beautiful pipeline for the release ritual you already know — not a magic black box.

Why shipx

Releasing a package is the same nine commands every time, in the same order, and you don't want to forget any of them or run them out of order.

  • Beautiful interactive UI built on @clack/prompts — spinners, prompts, and confirms that you actually enjoy looking at.
  • One config, every channel. npm, GitHub releases, Cargo workspaces (Tauri-friendly), and Homebrew tap formula — all from a single shipx.config.ts.
  • Stop on red. Preflight refuses to run on a dirty tree or the wrong branch. Each step is a single shell-out — no hidden state, no surprises.
  • Beta path. shipx --beta increments -beta.N and publishes with the beta dist-tag. Homebrew is skipped automatically.
  • Recoverable. If npm publish fails (auth, OTP, network), shipx drops into an interactive retry loop instead of aborting the whole pipeline.
  • Zero install. npx @lacymorrow/shipx runs against the project in your current directory.

Install

# Global
npm install -g @lacymorrow/shipx
shipx

# One-off
npx @lacymorrow/shipx

# In your repo
npm install --save-dev @lacymorrow/shipx

Requires Node ≥18. gh CLI is required for githubRelease. cargo-edit is required if you bump Cargo workspaces.

Usage

# Interactive — prompts for patch / minor / major
shipx

# Skip the prompt
shipx patch
shipx minor
shipx major

# Explicit version
shipx 2.0.0

# Beta release (publishes with --tag beta, skips Homebrew)
shipx --beta

# Multi-project deploy — scan parent dir, batch-publish with one OTP
cd ~/repo && shipx --multi

Tip

Set SHIPX_ROOT=/path/to/project to run shipx against a project other than the current directory — useful in monorepo automation.

Pipeline

flowchart LR
    A[preflight] --> B[bump version]
    B --> C[changelog]
    C --> D[commit + tag]
    D --> E[push]
    E --> F[GitHub release]
    F --> G[npm publish]
    G --> H[Homebrew]

    style A fill:#c026d3,stroke:#c026d3,color:#fff
    style H fill:#c026d3,stroke:#c026d3,color:#fff
Loading

Each step is independently toggleable. Set steps.<name>: false to skip it.

Configuration

shipx looks for config in this order, first hit wins:

  1. shipx.config.ts / shipx.config.js
  2. .shipxrc.json / .shipxrc
  3. "shipx" key in package.json
  4. Defaults (auto-detects package.json, src-tauri/Cargo.toml, and sibling ../homebrew-tap)

Example shipx.config.ts

import type { ShipConfig } from "@lacymorrow/shipx";

export default {
  packageJsonPaths: ["package.json", "packages/core/package.json"],
  bumpFiles: [
    {
      path: "bin/cli",
      pattern: /^VERSION="[^"]*"/m,
      replacement: (v) => `VERSION="${v}"`,
    },
  ],
  cargoWorkspaces: ["src-tauri"],
  steps: {
    homebrew: true,
    githubRelease: true,
  },
  git: {
    releaseBranch: "main",
    tagPrefix: "v",
    extraTags: ["cua-{tag}"],
    commitMessage: "release: {tag}",
  },
  npm: {
    access: "public",
  },
  homebrew: {
    tapPath: "../homebrew-tap",
    formulaFile: "Formula/mytool.rb",
    repoSlug: "user/repo",
  },
} satisfies ShipConfig;

All options

Option Type Default What it does
packageJsonPaths string[] Auto (["package.json"]) Paths to package.json files to bump
bumpFiles BumpFileConfig[] [] Additional files with regex-based version bumping
cargoWorkspaces string[] Auto (["src-tauri"] if exists) Cargo workspace dirs to bump via cargo set-version --workspace. Use [] to opt out.
steps.preflight boolean true Require clean tree + correct branch
steps.bumpVersion boolean true Update version in package.json + bump files
steps.changelog boolean true Generate changelog from git log since last tag
steps.commit boolean true Single commit for all bumped files
steps.tag boolean true Create git tag (plus any extraTags)
steps.push boolean true Push commit + tag(s) to origin
steps.githubRelease boolean true Create GitHub release via gh CLI
steps.npm boolean true Publish to npm (with interactive retry)
steps.homebrew boolean true Update Homebrew formula SHA + URL
git.releaseBranch string "main" Branch required for stable releases
git.tagPrefix string "v" Prefix prepended to the version
git.extraTags string[] [] Additional tags. Templates support {tag} (full, e.g. v0.5.3) and {version} (bare)
git.commitMessage string "release: {tag}" Commit message template
git.commitFlags string "--no-verify" Flags passed to git commit
git.pushFlags string "--no-verify" Flags passed to git push
npm.cwd string Project root Working directory for npm publish
npm.access "public" | "restricted" "public" npm publish access
homebrew.tapPath string Auto (sibling ../homebrew-tap) Path to your tap repo
homebrew.formulaFile string Auto-derived Formula file, relative to tapPath
homebrew.repoSlug string Auto-derived from origin owner/repo for the tarball URL
homebrew.commitMessage string "{formula}: update to {tag}" Tap commit message template

Recipes

Tauri / Cargo workspace

shipx auto-detects src-tauri/Cargo.toml and adds it to cargoWorkspaces. Requires cargo install cargo-edit.

// shipx.config.ts
export default {
  cargoWorkspaces: ["src-tauri"], // explicit; or omit for auto-detection
  steps: { npm: false }, // Tauri apps usually don't publish to npm
} satisfies ShipConfig;

Monorepo with coupled versioning

All packages bump to the same version:

export default {
  packageJsonPaths: [
    "package.json",
    "packages/core/package.json",
    "packages/cli/package.json",
  ],
} satisfies ShipConfig;

For independently-versioned monorepos, use changesets instead — shipx isn't built for that.

Homebrew tap

Drop your tap repo next to your project as a sibling (../homebrew-tap) and shipx finds it. Or configure it explicitly:

export default {
  homebrew: {
    tapPath: "../homebrew-tap",
    formulaFile: "Formula/mytool.rb",
    repoSlug: "lacymorrow/mytool",
  },
} satisfies ShipConfig;

shipx downloads the tarball, computes SHA256, updates url/sha256 in the formula, commits, and pushes from the tap.

Multi-project deploy

Got a bunch of repos in ~/repo/? Deploy them all at once:

cd ~/repo
shipx --multi

shipx scans for subdirectories with a package.json, detects which have unreleased commits, and lets you pick which to release. The killer feature: npm publishes are batched — enter your OTP once and it's reused across all packages, so your 2FA code doesn't expire mid-deploy.

The flow:

  1. Select projects — sorted by change count, with dirty/private indicators
  2. Pick versions — individually, or apply the same bump type to all
  3. Prepare — each project gets its own bump → commit → tag → push → GitHub release
  4. Batch publish — all npm publishes happen back-to-back with a shared OTP
  5. Homebrew — formulas updated for non-beta releases

Combine with --beta for beta batch releases: shipx --multi --beta.

Beta release

shipx --beta
  • If current version is 1.0.0, becomes 1.0.0-beta.0
  • If current version is 1.0.0-beta.0, becomes 1.0.0-beta.1
  • Publishes to npm with --tag beta (your latest dist-tag is untouched)
  • Skips the branch check in preflight (release from any branch)
  • Skips Homebrew automatically

Comparison

shipx np release-it changesets
Interactive UI ✅ (@clack) ✅ (Listr) partial
npm publish
GitHub release via Action
Cargo workspaces via plugin
Homebrew formula via plugin
Beta / pre-release
Multi-project batch deploy
Multi-package monorepo (coupled)
Multi-package monorepo (independent)
Changelog from PR labels via plugin
Zero plugins required

TL;DR — Use shipx for single-package or coupled-version projects that ship to multiple registries (especially Cargo + Homebrew). Use changesets for independently-versioned monorepos. Use np if you want a smaller, npm-only tool.

FAQ

How is shipx different from np?

np is excellent and the spiritual predecessor of shipx. The differences:

  • shipx uses @clack/prompts instead of Listr — the UI feels more modern.
  • shipx bumps Cargo workspaces and updates Homebrew formulas out of the box. np is npm-only.
  • shipx is intentionally tiny (~600 lines of TS, two runtime deps). np is more battle-tested with more options.
What if npm publish fails?

shipx drops into an interactive retry loop with four options:

  1. Enter OTP — for 2FA accounts (validates that you typed 6 digits)
  2. Log in to npm — runs npm login, then retries
  3. Retry — just try again (good for transient errors)
  4. Skip — abandon npm publish and continue to remaining steps

Everything before npm publish (the bump, commit, tag, push, GitHub release) is already done — you can always re-publish manually.

Can I disable a step?

Yes. Every step in the pipeline has a steps.<name> boolean flag. Set it to false to skip:

export default {
  steps: { homebrew: false, githubRelease: false },
} satisfies ShipConfig;
Does shipx sign tags or commits?

shipx delegates to your local git config. Set commit.gpgsign=true / tag.gpgsign=true and your tags will be signed. The default commitFlags / pushFlags of --no-verify is overrideable via config if you have hooks you actually want to run.

Can I extract the changelog before shipping?

shipx generates a changelog from git log <last-tag>..HEAD --pretty=format:"- %s (%h)" and uses it as the GitHub release body. It's printed to the terminal during the release. If you need a CHANGELOG.md file, write your own bumpFile entry — or use git-cliff before invoking shipx.

Related

Other projects by the author:

  • album-art — Fetch an album or artist image URL.
  • crossover — A crosshair overlay for any screen.
  • cinematic — Gorgeous desktop movie collections.

Acknowledgments

shipx stands on the shoulders of:

Contributing

Bug reports and pull requests welcome. See CONTRIBUTING.md and the security policy. For a high-level architecture overview, see CLAUDE.md.

License

MIT © Lacy Morrow

If shipx saved you time, consider sponsoring on GitHub, supporting on Patreon, or buying a coffee.

About

Interactive release CLI — bump, tag, publish, ship. npm · Cargo · Homebrew · GitHub.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors