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.
Supported config files
Section titled “Supported config files”pubm supports these file names:
pubm.config.tspubm.config.mtspubm.config.ctspubm.config.jspubm.config.mjspubm.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.
Default config
Section titled “Default config”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",});How registries are inferred
Section titled “How registries are inferred”pubm inspects manifest files in each package directory and maps them to registries:
| Manifest | Inferred registry |
|---|---|
package.json | npm |
jsr.json | jsr |
deno.json / deno.jsonc | jsr |
Cargo.toml | crates |
Two additional rules apply for npm:
publishConfig.registryinpackage.jsonreplacesnpmwith that custom URL.npmrcregistry replacesnpmwhenpublishConfigis absent
Top-level options
Section titled “Top-level options”versioning
Section titled “versioning”- Type:
"independent" | "fixed" - Default:
"independent"
Controls how versions are assigned in multi-package repositories.
branch
Section titled “branch”- Type:
string - Default:
"main"
Branch that must match HEAD during guarded release runs.
packages
Section titled “packages”- Type:
PackageConfig[] - Default:
[{ path: "." }]
Explicitly declares publishable packages. Registries are inferred from manifest files unless overridden per package.
plugins
Section titled “plugins”- 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.
linked
Section titled “linked”- Type:
string[][] - Default:
[]
Groups of packages that should share the highest bump type.
ignore
Section titled “ignore”- Type:
string[] - Default:
[]
Package names or paths to exclude from workspace discovery.
- Type:
string - Default:
"latest"
Default dist-tag for publish runs.
contents
Section titled “contents”- Type:
string - Default:
"."
Directory to chdir into before running the release pipeline.
saveToken
Section titled “saveToken”- Type:
boolean - Default:
true
Whether supported registry tokens should be stored for reuse on local machines.
releaseDraft
Section titled “releaseDraft”- Type:
boolean - Default:
true
Whether the publish flow should create a GitHub Release. When disabled, the release step is skipped entirely.
excludeRelease
Section titled “excludeRelease”- 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/*" }, ],});validate
Section titled “validate”- 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.
snapshotTemplate
Section titled “snapshotTemplate”- 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 tosnapshot{timestamp}: UTC timestamp inYYYYMMDDTHHmmssformat{commit}: current git commit SHA
createPr
Section titled “createPr”- 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.
locale
Section titled “locale”- 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".
rollback
Section titled “rollback”Controls rollback behavior when publish fails.
| Key | Type | Default | Description |
|---|---|---|---|
strategy | "individual" | "all" | "individual" | Rollback granularity. Replaces the deprecated top-level rollbackStrategy. |
dangerouslyAllowUnpublish | boolean | false | Allow 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.
How config and CLI flags interact
Section titled “How config and CLI flags interact”The current runtime behavior is:
- config provides repository defaults for
packagesandplugins - versioning-related config such as
versioning,fixed, andlinkedis used by changeset flows - CLI flags still control per-run publish behavior
- registries are inferred from manifests;
packages[].registriesoverrides that inference
For a one-off publish to a specific registry:
pubm --registry npm --tag betaMulti-package configuration
Section titled “Multi-package configuration”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
pubmto 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
PackageConfig
Section titled “PackageConfig”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" } }, ], },];Versioning strategies
Section titled “Versioning strategies”Independent versioning
Section titled “Independent versioning”Each package gets its own version and tag.
export default defineConfig({ versioning: "independent",});Fixed versioning
Section titled “Fixed versioning”Every affected package shares one version.
export default defineConfig({ versioning: "fixed",});Fixed and linked groups
Section titled “Fixed and linked groups”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 versionlinked: the highest bump type propagates across the group
Snapshot settings
Section titled “Snapshot settings”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
Plugins
Section titled “Plugins”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.
Example patterns
Section titled “Example patterns”Simple library
Section titled “Simple library”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.).
App monorepo
Section titled “App monorepo”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" }, ],});Mixed JS + Rust workspace
Section titled “Mixed JS + Rust workspace”import { defineConfig } from "@pubm/core";
export default defineConfig({ versioning: "independent", packages: [ { path: "packages/sdk" }, { path: "crates/sdk-core" }, ],});Release assets
Section titled “Release assets”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", ],});compress
Section titled “compress”- 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", ],});releaseAssets
Section titled “releaseAssets”- 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.
Version sources
Section titled “Version sources”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.
versionSources
Section titled “versionSources”- Type:
"all" | "changesets" | "commits" - Default:
"all"
Controls which sources pubm changesets version reads when calculating version bumps.
| Value | Behavior |
|---|---|
"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. |
conventionalCommits
Section titled “conventionalCommits”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 type | Bump |
|---|---|
feat | minor |
fix | patch |
perf | patch |
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.
Next steps
Section titled “Next steps”- 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.