Skip to content

Configuration

pubm is CLI-first by design, but real projects quickly outgrow flag-only configuration. pubm.config.* is where you keep repository-level publish targets, package layout, plugins, and changeset-related versioning rules.

Run pubm init to generate this configuration file interactively. The wizard only writes a config file when your choices differ from the defaults shown below.

pubm supports these file names:

  1. pubm.config.ts
  2. pubm.config.mts
  3. pubm.config.cts
  4. pubm.config.js
  5. pubm.config.mjs
  6. pubm.config.cjs

TypeScript config files are supported directly. The loader bundles the file before evaluation, so you do not need a separate runtime transpilation step.

These are the defaults resolved by the config layer:

export default defineConfig({
versioning: "independent",
branch: "main",
changelog: true,
changelogFormat: "default",
commit: false,
access: "public",
fixed: [],
linked: [],
updateInternalDependencies: "patch",
ignore: [],
tag: "latest",
contents: ".",
saveToken: true,
releaseDraft: true,
releaseNotes: true,
rollback: {
strategy: "individual",
dangerouslyAllowUnpublish: false,
},
packages: [{ path: "." }],
validate: {
cleanInstall: true,
entryPoints: true,
extraneousFiles: true,
},
snapshotTemplate: "{tag}-{timestamp}",
locale: "en",
});

pubm inspects manifest files in each package directory and maps them to registries:

ManifestInferred registry
package.jsonnpm
jsr.jsonjsr
deno.json / deno.jsoncjsr
Cargo.tomlcrates

Two additional rules apply for npm:

  • publishConfig.registry in package.json replaces npm with that custom URL
  • .npmrc registry replaces npm when publishConfig is absent
  • Type: "independent" | "fixed"
  • Default: "independent"

Controls how versions are assigned in multi-package repositories.

  • Type: string
  • Default: "main"

Branch that must match HEAD during guarded release runs.

  • Type: PackageConfig[]
  • Default: [{ path: "." }]

Explicitly declares publishable packages. Registries are inferred from manifest files unless overridden per package.

  • Type: PubmPlugin[]
  • Default: []

Registers plugins that can add hooks, commands, registries, or ecosystems.

  • Type: string[][]
  • Default: []

Groups of packages that must always resolve to the same version.

  • Type: string[][]
  • Default: []

Groups of packages that should share the highest bump type.

  • Type: string[]
  • Default: []

Package names or paths to exclude from workspace discovery.

  • Type: string
  • Default: "latest"

Default dist-tag for publish runs.

  • Type: string
  • Default: "."

Directory to chdir into before running the release pipeline.

  • Type: boolean
  • Default: true

Whether supported registry tokens should be stored for reuse on local machines.

  • Type: boolean
  • Default: true

Whether the publish flow should create a GitHub Release. When disabled, the release step is skipped entirely.

  • Type: string[]
  • Default: []

Glob patterns for packages that should be versioned and published but excluded from git tag creation and GitHub Release drafts. Applies only when versioning is "independent".

Use this for internal build artifacts, such as platform-specific binaries, that are published to a registry but should not appear as standalone releases.

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

These fields are part of the public schema, but they are not yet active toggles across every publish path. Treat them as typed config surface, not guaranteed runtime switches.

  • Type: string
  • Default: "{tag}-{timestamp}"

Template used when pubm --snapshot generates a temporary version string.

The base version from package.json is always automatically prepended to the snapshot version. The template controls only the suffix that follows the base version.

Available template variables:

  • {tag}: snapshot tag, defaulting to snapshot
  • {timestamp}: UTC timestamp in YYYYMMDDTHHmmss format
  • {commit}: current git commit SHA
  • Type: boolean
  • Default: false

Create a pull request for the version bump commit instead of pushing directly to the base branch. When enabled, pubm creates a pubm/version-packages-* branch, pushes it, and opens a PR. The release workflow triggers normally when the PR is merged.

Can also be set as a fallback: pubm automatically falls back to PR mode when a direct push to a protected branch fails.

  • Type: "en" | "ko" | "zh-cn" | "fr" | "de" | "es"
  • Default: "en"

CLI output language. Can also be set via the --locale flag or the PUBM_LOCALE environment variable. Resolution order: --locale flag → PUBM_LOCALE env → config → system locale → "en".

Controls rollback behavior when publish fails.

KeyTypeDefaultDescription
strategy"individual" | "all""individual"Rollback granularity. Replaces the deprecated top-level rollbackStrategy.
dangerouslyAllowUnpublishbooleanfalseAllow registry unpublish/yank during rollback in non-TTY (CI) environments. Even after unpublish, npm permanently reserves the version number: it cannot be reused.
export default defineConfig({
rollback: {
strategy: "individual",
dangerouslyAllowUnpublish: true, // opt in for CI
},
});

The deprecated top-level rollbackStrategy field still works and maps to rollback.strategy. If both are set, rollback.strategy takes precedence.

The current runtime behavior is:

  • config provides repository defaults for packages and plugins
  • versioning-related config such as versioning, fixed, and linked is used by changeset flows
  • CLI flags still control per-run publish behavior
  • registries are inferred from manifests; packages[].registries overrides that inference

For a one-off publish to a specific registry:

Terminal window
pubm --registry npm --tag beta

For workspaces or mixed-language repositories, declare packages explicitly. Registries are inferred from each package’s manifest files:

import { defineConfig } from "@pubm/core";
export default defineConfig({
versioning: "independent",
packages: [
{
path: "packages/core",
},
{
path: "packages/cli",
registries: ["npm"],
},
{
path: "crates/pubm-rs",
},
],
});

Use explicit package entries when:

  • you want pubm to publish only a subset of discovered workspace packages
  • JavaScript and Rust packages live in the same repository
  • you need to override inferred registries or add private registries

Package-level entries are shaped like this:

interface PackageConfig {
path: string;
registries?: Array<"npm" | "jsr" | "crates" | PrivateRegistryConfig>;
ecosystem?: "js" | "rust";
buildCommand?: string;
testCommand?: string;
}
interface PrivateRegistryConfig {
url: string;
token: { envVar: string };
}

Use registries only when inference is insufficient or when you need to add private registries alongside built-in ones:

packages: [
{
path: "packages/a",
registries: [
"npm",
{ url: "https://npm.internal.com", token: { envVar: "INTERNAL_NPM_TOKEN" } },
],
},
];

Each package gets its own version and tag.

export default defineConfig({
versioning: "independent",
});

Every affected package shares one version.

export default defineConfig({
versioning: "fixed",
});

You can keep most packages independent while forcing selected packages to move together:

export default defineConfig({
versioning: "independent",
fixed: [["@acme/core", "@acme/react"]],
linked: [["@acme/cli", "@acme/config"]],
});
  • fixed: all packages in the group end up on the exact same version
  • linked: the highest bump type propagates across the group

Snapshots are good for preview releases and branch-specific validation.

export default defineConfig({
snapshotTemplate: "{tag}-{timestamp}",
});

The base version is prepended automatically, so a template of "{tag}-{timestamp}" produces output like:

  • 0.4.1-snapshot-20260312T102233

Add plugins through the plugins array:

import { defineConfig } from "@pubm/core";
import { externalVersionSync } from "@pubm/plugin-external-version-sync";
export default defineConfig({
plugins: [
externalVersionSync({
targets: [
{
file: "README.md",
pattern: /pubm@([\\w.-]+)/,
},
],
}),
],
});

Read the Plugins API Reference for hook order, command registration, and custom registry support.

If the package has package.json and jsr.json (or deno.json / deno.jsonc), registries are inferred automatically. No config file is needed. pubm.config.ts is optional. Only create it when you need to override a default (custom branch, plugins, release assets, etc.).

Packages with only package.json resolve to npm. No explicit registries needed unless you want to override.

import { defineConfig } from "@pubm/core";
export default defineConfig({
versioning: "fixed",
packages: [
{ path: "packages/web" },
{ path: "packages/server" },
],
});
import { defineConfig } from "@pubm/core";
export default defineConfig({
versioning: "independent",
packages: [
{ path: "packages/sdk" },
{ path: "crates/sdk-core" },
],
});

releaseAssets declares the binary artifacts to attach to each GitHub Release. A simple glob string is enough for most cases:

import { defineConfig } from "@pubm/core";
export default defineConfig({
releaseAssets: [
"platforms/*/bin/mytool",
],
});
  • Type: "tar.gz" | "zip" | "tar.xz" | "tar.zst" | false | Record<string, CompressFormat>
  • Default: OS-aware auto-detect (windows → zip, others → tar.gz)

Global default compression format. Can be overridden at the group or file level inside releaseAssets.

export default defineConfig({
// Use zip for Windows, tar.gz everywhere else
compress: { windows: "zip" },
releaseAssets: [
"platforms/*/bin/mytool",
],
});
  • Type: (string | ReleaseAssetGroupConfig)[]
  • Default: undefined (no assets attached)

Each entry is either a glob string or an object with explicit control:

releaseAssets: [
// String: glob, fully automatic
"platforms/*/bin/mytool",
// Object: explicit per-group settings
{
packagePath: "packages/cli", // Monorepo: link to this package's release
files: [
"platforms/*/bin/mytool",
{ path: "dist/*.dmg", compress: false, name: "MyTool-{version}-{os}" },
],
compress: "tar.gz", // Group default
name: "{name}-{platform}", // Group name template
},
]

Read the Release Assets guide for the full compress cascade rules, name template variables, and use-case examples.

pubm changesets version can resolve version bumps from two sources: changeset files in .pubm/changesets/ and conventional commit messages. You can control which sources are active and how commit types map to bump levels.

  • Type: "all" | "changesets" | "commits"
  • Default: "all"

Controls which sources pubm changesets version reads when calculating version bumps.

ValueBehavior
"all"Uses changesets for packages that have them. Falls back to conventional commits for packages with no pending changesets.
"changesets"Only reads .pubm/changesets/ files. Conventional commits are ignored.
"commits"Only reads conventional commit messages. Changeset files are ignored.

Configures how commit type prefixes map to semver bump levels. Only applies when versionSources is "all" or "commits".

export default defineConfig({
versionSources: "all",
conventionalCommits: {
types: {
// Add types that are ignored by default
refactor: "patch",
docs: "patch",
},
},
});

The default mapping is:

Commit typeBump
featminor
fixpatch
perfpatch

Commits with BREAKING CHANGE in the footer or a ! suffix (e.g. feat!:) always produce a major bump regardless of the type mapping.

Commit types not listed in the mapping (e.g. chore, docs, test, ci, style, refactor) are ignored during bump calculation. You can override any type in the types object — for example, refactor: "patch" would treat refactor commits as patch bumps.

  • Use the Monorepo guide if you need package grouping or mixed registries.
  • Use the Plugins API Reference if your config registers custom plugins.
  • Use the Release Assets guide to attach binaries and archives to GitHub Releases.
  • See the Changesets guide for more on the conventional commit fallback behavior.