V3 migration guide
This page describes how to migrate from beachball v2 to v3.
Overview and installation
As of writing, beachball v3 is in alpha under the next tag. You can see the latest version via npm show beachball@next version.
It's currently recommended to pin a specific version: the prerelease has some significant benefits (not easily backported) for secure publishing, and most of the true breaking changes have already been implemented, but there may still be some changes to dependent bumping behavior which may require further verification.
This release mainly focuses on:
- Supporting secure publishing: better approaches for passing git and npm tokens, help with generating a GitHub App token, and (planned) better support for separate bump/publish steps
- Fixing longstanding oddities (most of the option behavior changes)
- Reducing dependency tree size (
yargs-parser => commander,execa => nano-spawn,minimatch => path.matchesGlob) - Improving npm performance (planned) and reducing git history bloat (no
CHANGELOG.jsonby default)
The remaining planned changes will likely focus on:
- Further npm improvements: respect registry and credentials from npm config, and don't use CLI for registry read operations
- Better support for separate bump/publish steps
- Possibly: dependent bumping behavior coherency improvements (this is the main reason for keeping the version pinned)
Running the migrate command
beachball v3 includes a migrate command that checks your config and logs any updates needed for v3:
beachball migrate
If your config is already compatible, you will see:
No config updates are needed for v3.
Otherwise, the command will list specific config updates that are needed. The command does NOT attempt to make updates directly due to the variety of locations and file types where the config can be specified.
Breaking changes
Many of the config-related changes below are detected by beachball migrate or automatic validation.
Require Node.js 22.18+
beachball v3 requires Node.js 22.18.0 or newer, and its published output uses ES2024 syntax. This also allows support for TypeScript or ESM config files.
CLI option parsing changes
CLI parsing moved from yargs-parser to commander. Most behavior is preserved, but a few things changed:
- Unknown options now cause an error instead of being silently passed through. This should have no impact unless you were passing extra
BeachballOptionsvalues on the command line that aren't actually specified inCliOptions. (If there's some other value you need as a CLI option, please open an issue or PR.) - Repeated non-array options use the last value instead of throwing an error.
- String boolean values are no longer supported: Specifying boolean values like
--push falseor--push=falsewon't work. Use--push/--no-push(or similar) instead. - If you're using
--camelCaseflags, it's recommended to switch to--kebab-case.camelCaseis not supported by default bycommander, andbeachball's workarounds for this may be removed in a future major version.
CHANGELOG.json generation disabled by default
generateChangelog now defaults to 'md', meaning CHANGELOG.json files are no longer generated by default. This is because most repos don't use CHANGELOG.json, and those files add substantial git history bloat in a large/busy repo. (CHANGELOG.md is still generated.)
- If you rely on
CHANGELOG.json, setgenerateChangelog: truein your config. - If you don't, delete any existing
CHANGELOG.jsonfiles (beachball migratewon't do this automatically).
registry and access respect npm settings
In v2, Beachball defaulted registry to https://registry.npmjs.org/ and access to restricted.
In v3, Beachball no longer sets its own defaults for either option:
registrynow uses whatever registrynpmis configured to use.- Temporary exception: Due to unfinished features, if you're explicitly providing
BEACHBALL_NPM_TOKENor--token, you must explicitly provideregistry.
- Temporary exception: Due to unfinished features, if you're explicitly providing
accessnow uses npm's default publish behavior. Since scoped packages default torestricted, you should almost always set this topublic(as most repos with scoped packages already do).
Fix group exclude negation behavior
Remove the requirement for groups[*].exclude and changelog.groups[*].exclude patterns to be negated (leading !).
To migrate, simply remove the leading ! from all exclude patterns.
Stricter comparison remote/branch resolution
The logic for determining the comparison remote and branch is stricter: beachball now throws if no remotes are defined, or if the root package.json specifies a repository field but no matching remote is found. If your branch option contains a /, beachball checks whether the leading segment matches a configured remote name, and falls back to the default remote otherwise.
Custom changelog rendering changes
Only relevant for custom changelog renderers (or if reading CHANGELOG.json later): PackageChangelog.tag and ChangelogJsonEntry.tag are now undefined when the package had no associated git tag (previously a value was always present).
ChangelogEntry.commit will be undefined if the package did not have an associated commit.
Stop writing placeholder commit hashes in changelog
In v2, Beachball could write "not available" to the commit field in CHANGELOG.json when a commit hash was unavailable.
In v3, Beachball omits the commit field entirely in those cases instead, including dependent bump entries that do not have a real commit hash yet.
Stop writing default dependentChangeType in change files
Beachball no longer writes the default dependentChangeType values to generated change files. The same defaults as before are applied at bump time (none when the change type is none, or patch otherwise). Existing change files which specify dependentChangeType are still supported, and --dependent-change-type continues to write an explicit override.
Custom tools that read change files should allow dependentChangeType to be missing and apply the same defaults. Tools that create change files should omit the field unless non-default dependent bump behavior is intended.
Fix fallback behavior for disallowed change types
Fallback behavior for disallowed change types now keeps stable and prerelease types separate. Stable types fall back from major -> minor -> patch -> none; prerelease types fall back from premajor -> preminor -> prepatch -> prerelease -> none. (Previously, the combined ordering could cause a disallowed stable type to fall back to a prerelease type, such as minor becoming preminor rather than patch.)
This is technically a breaking change, but really it's more of a bug fix, since the behavior was probably not intentional and is almost never desirable.
shouldPublish behavior change
Note: you should almost never need this option - in most scenarios, just set
private: trueinpackage.jsoninstead. Only use this if the package should not be published to npm, but you do want beachball to manage its version and changelog.
In v2, the beachball.shouldPublish: false package option was handled inconsistently: packages didn't get change files or direct version bumps, but if a shouldPublish: false package was a dependent of a bumped package, it was silently bumped and published anyway.
In v3, shouldPublish: false packages are full participants in all steps of the workflow except npm publish:
- Change files are generated and required
- Version bumps, git tags, and changelogs are produced (same as published packages)
- The final
npm publish(orpack) step is skipped - If a published package has a
shouldPublish: falsepackage in its production dependencies, Beachball will exit with an error (same as withprivate: truedeps) - Since
shouldPublish: falseis redundant withprivate: true,beachball migratereports this as an error
BeachballConfig and other type updates
- Rename
changelog.groups[*].masterPackageNametomainPackageName. - Removed rarely-used options:
new: This option was never needed if PR builds runbeachball check(a new package without a change file already causes an error), and it had a significant performance cost because it checked the registry for all unmodified packages.packStyle: packing always uses the layered style now.
hooks.prebumpno longer receivespackageInfos. This was never in the signature, and trying to modify it may lead to unexpected behavior. Please open an issue if you were using this and we can find an alternative.taganddefaultNpmTagupdates:tagmay not benull. This was already ignored in the previous version, but now it's removed from the types.- Note that
tag: ''means fall back todefaultNpmTagor'latest'(npm doesn't allow publishing a new version without adist-tag). - To disable git tags for a package, set
gitTags: false.
- Note that
defaultNpmTagshould not be''for reasons above.
- Package-level-only options have been removed from
BeachballConfig.
Config loading changes
Beachball v3 replaces cosmiconfig with custom logic which supports most previous config file names but removes support for YAML config files. TypeScript config files are now supported and will be run natively in Node.
The config file is now only resolved at the project root. Support for searching up from the project root (into parent directories) has been removed.
Other potentially-breaking changes
- Use Node's
path.matchesGlobinstead ofminimatch. Behavior should stay the same, but if not, please open an issue. - If you're deep importing beachball's internal helpers, some deprecated signatures have been removed. Use the new signatures (which pre-calculate and share context) instead.
Other changes
New options
- Add
getGitTagoption, a function used to generate per-package git tags. - Add
commitMessageoption, a function used to generate the commit message for thechangeandpublishcommands (overridden by--messageforpublish). - Support authenticating with a git token from
BEACHBALL_GIT_TOKEN.
New commands
- Add experimental
beachball publish-helpers update-lock-registrycommand
Patches
- In the change prompt, add a link to the change type help page.
- Use
p-graphv3 for both concurrent and non-concurrent publishing, and for generating graph layers when packing. - Replace
execawithnano-spawn. - Use
--no-tagsoption withgit fetch. - Update the layer logic to only consider the set of published packages when building its graph.
- Use
node:prefix on built-in imports.