prp
prp (please release please) manages versions, changelogs and publishing for monorepos with npm packages, Cargo crates and Deno (JSR) packages.

Why?
prp was built to make publishing many packages, written in more than one language, less painful.
1. Versioning
Once packages start depending on each other, managing their versions by hand gets hard quickly. prp reads the dependency graph between packages and decides how far each one has to be bumped.
For example, packages can be grouped in two ways.
fixed packages are always released together, with one version: the highest version in the group, bumped.
[]
= [["packages/a", "packages/b"]]
With a at 1.0.0 and b at 1.2.0, a minor bump of a releases both at 1.3.0.
linked packages keep their own versions, but share the highest bump when they are released together. A package without changes is not released.
[]
= [["packages/a", "packages/b", "packages/c"]]
With a at 1.0.0, b at 2.0.0 and c at 3.0.0, a minor bump of a and a patch bump of b become a minor bump of both: a is released at 1.1.0 and b at 2.1.0. c has no changes and stays at 3.0.0.
The concepts and names of fixed and linked are borrowed from sampo.
A fix for an older major version, such as a security patch, may need to be backported. With a release branch configured, prp bumps from the versions on that branch and opens the release pull request against it. Publish such a release with prp release --dist-tag <tag> so that npm's latest stays on the newest line.
2. Changes
Keeping track of changes in a busy monorepo is not easy, and one change can affect several packages, which takes a lot of effort to write down for each of them.
prp finds the changes of each package since its previous version from git tags. They are written from commit messages by default. For more context, write a detailed note with prp change.
Installation
Install PRP using Cargo:
Or from npm:
Getting Started
Initialize PRP in your repository:
This command creates a config file at .prp/config.toml, with the packages found in package.json workspaces, pnpm-workspace.yaml, the Cargo workspace or the Deno workspace:
.prp/
└─ config.toml
A release then goes like this:
Configuration
# The workspace root, relative to the working directory.
# Default: the working directory
= "."
[]
# The branch release pull requests target.
# Default: "main"
= "main"
# Other branches allowed to release, as globs. Empty allows every branch.
# Default: []
= ["release/*"]
# The tag of a released version. Placeholders: {package_name}, {version}.
# Default: "{package_name}-v{version}"
= "{package_name}@{version}"
[]
# The GitHub API, to use GitHub Enterprise Server.
# Default: "https://api.github.com"
= "https://api.github.com"
# The owner of the repository.
# Default: taken from the `origin` remote
= "acme"
# The name of the repository.
# Default: taken from the `origin` remote
= "monorepo"
# Release without creating GitHub releases.
# Default: false
= false
# The branch of a release pull request. Placeholders: {branch}, {commit}.
# Default: "prp/{branch}_{commit}"
= "release/{branch}"
# The title of a release pull request and its commit. Placeholders: {num}.
# Commits starting with its text before the first placeholder count as release commits.
# Default: "release: {num}(s) packages"
= "release: {num} packages"
# Labels added to a release pull request.
# Default: []
= ["release"]
[]
# Also write every release to the root changelog.
# Default: true
= true
# The root changelog, relative to the workspace root.
# Default: "CHANGELOG.md"
= "CHANGELOG.md"
# The changelog of a package, relative to its directory.
# Default: "CHANGELOG.md"
= "CHANGELOG.md"
# How a commit is listed. Placeholders: {summary}, {body}, {sha}, {author_name},
# {author_email}, {committer_name}, {committer_email}.
# Default: ["- {summary} by {author_name} ({sha})"]
= ["- {summary} ({sha})"]
# Put before the title of a breaking change file.
# Default: "**[BREAKING]** "
= "**[BREAKING]** "
# List the commits of a release.
# Default: true
= true
# Link the registry pages of the published packages.
# Default: true
= true
[]
# Directories of the packages, as globs. Empty finds every versioned manifest.
# Required
= ["packages/*", "crates/*"]
# Directories to leave out, as globs.
# Default: []
= ["packages/internal/*"]
# Packages always released together, with one version.
# Default: []
= [["packages/core", "packages/cli"]]
# Packages sharing the highest bump when released together.
# Default: []
= [["packages/react", "packages/vue"]]
# Files whose changes alone do not release a package.
# Default: ["**/*.md"]
= ["**/*.md", "**/tests/**"]
# Settings of one package, by its directory.
[]
# The name used in tags, changelogs and output.
# Default: the directory, or "root" for the workspace root
= "node"
# Replaces git.release_tag_format for this package.
# Default: git.release_tag_format
= "node/{version}"
# Manifests of the package, as globs relative to its directory.
# Default: the manifests directly in the directory
= ["package.json", "npm/*/package.json"]
# Commands run in the package directory before it is published.
# Default: []
= [{ = "yarn", = ["build"] }]
# Files uploaded to the GitHub release, as globs relative to the package directory.
# Default: []
= ["artifacts/*.node"]
# Replaces packages.changes_ignore for this package.
# Default: packages.changes_ignore
= ["**/*.md"]
# Release the package without a GitHub release.
# Default: false
= false
# Bump the versions but do not publish to a registry.
# Default: false
= false
[]
# The User-Agent of HTTP requests.
# Default: "prp/<version>"
= "prp"
# Seconds before an HTTP request gives up.
# Default: no timeout
= 30
[]
# The registry versions are looked up in.
# Default: "https://registry.npmjs.org"
= "https://registry.npmjs.org"
# The registry packages are published to.
# Default: the package manager's own setting
= "https://npm.pkg.github.com"
# The command publishing a package, run in its directory.
# Default: the command of the package manager in use, such as "npm publish"
= "yarn npm publish --access=public --provenance"
[]
# The sparse index crate versions are looked up in.
# Default: "https://index.crates.io"
= "https://index.crates.io"
[]
# The JSR API versions are looked up in.
# Default: "https://api.jsr.io"
= "https://api.jsr.io"
# The command publishing a package, run in its directory.
# Default: "deno publish --allow-dirty"
= "deno publish --allow-dirty --allow-slow-types"
Commands
| Command | Description |
|---|---|
prp init |
Create .prp/config.toml |
prp status |
List packages, their groups and their latest published versions |
prp change |
Write a change file for the current commit |
prp prepare |
Choose versions, update manifests and changelogs, and open a release pull request |
prp release |
Publish packages, push tags and create GitHub releases |
prp prepare opens an editor to review the versions. Pass --auto to skip it, which is required outside a terminal.
Exit codes
| Exit Code | Description |
|---|---|
0 |
Success |
1 |
Error |
2 |
No changes - command succeeded but no action was needed |
License
MIT License