Skip to content

Monorepo

pubm treats monorepos as a core use case. It discovers packages, understands dependency order, and can release JavaScript, Rust, and Deno workspaces from the same repository.

Workspace discovery currently looks for:

  • pnpm-workspace.yaml
  • Cargo.toml with [workspace]
  • deno.json or deno.jsonc with workspace
  • package.json with a workspaces field
  • bunfig.toml together with package.json workspaces to classify Bun workspaces

After discovery, pubm builds a dependency graph and uses it to determine publish order.

When to rely on discovery vs. explicit package config

Section titled “When to rely on discovery vs. explicit package config”

Automatic discovery works well when:

  • every workspace package is publishable
  • you do not need to mix ecosystems with selective publish targets

Use explicit packages config when:

  • only some workspace packages should publish
  • Rust crates live alongside JS packages
  • you want to override inferred registries or add private registries
  • you want the docs and release pipeline to stay explicit

Registries are inferred from each package’s manifest files. You only need registries entries in packages when the inferred set is wrong or when private registries are needed.

For dependent packages, pubm publishes in topological order.

This matters because:

  • npm and jsr consumers can install the new dependency version immediately
  • crates.io publishing must respect crate dependency ordering
  • partially ordered publishes are easier to roll back and diagnose

If a circular dependency prevents a safe publish order, pubm fails instead of guessing.

export default defineConfig({
versioning: "independent",
});
  • each package can receive a different version
  • each package is tagged independently
  • changesets affect only the packages they mention unless groups change that result
export default defineConfig({
versioning: "fixed",
});
  • packages released together share one version
  • the repository gets one release tag such as v1.8.0
export default defineConfig({
versioning: "independent",
fixed: [["@acme/core", "@acme/react", "@acme/vue"]],
linked: [["@acme/cli", "@acme/config"]],
});
Terminal window
pubm init --changesets
pubm changesets add
pubm

In a monorepo, the important difference is not the command shape but the release plan that pubm calculates from pending changesets, package groups, and dependency order.

The most important rule is simple: put packages in the changeset file, not in commit messages or PR titles.

pubm changesets add will:

  • discover available packages
  • let you choose one or more packages
  • prompt for the bump type for each selected package
  • record the release summary

The standard pubm release pipeline then consumes those pending changesets during versioning.

Each package’s registries are inferred from its manifest files. A package with both package.json and jsr.json resolves to both npm and jsr. A deno.json or deno.jsonc also resolves to jsr. A Cargo.toml resolves to crates. Use registries only to override or extend.

Tag format depends on the resulting version plan.

v1.4.0
@acme/core@1.4.0
@acme/react@1.2.0

In independent mode, every package normally receives its own git tag and GitHub Release draft. Use excludeRelease to opt specific packages out of that behavior while still versioning and publishing them.

export default defineConfig({
versioning: "independent",
excludeRelease: ["packages/cli/platforms/*"],
packages: [
{ path: "packages/core" },
{ path: "packages/cli" },
{ path: "packages/cli/platforms/*" },
],
});

Packages matched by excludeRelease are still bumped and published to their registries. They are only skipped for git tag creation and GitHub Release drafts.

  • Read the Changesets guide to understand how version decisions are recorded.
  • Read the Configuration guide for package-level config and grouping options.