bumpp
v12.3.0
Published
Bump version, commit changes, tag, and push to Git
Maintainers
Readme
bumpp
Forked from version-bump-prompt
Changes in this fork
- Renamed to
bumpp- so you can usenpx bumppdirectly. - Use the current version's
preidwhen available. - Confirmation before bumping.
- Enable
--commit--tag--pushby default. (opt-out by--no-push, etc.) -ror--recursiveto bump all packages in the monorepo.--executeto execute the command, or execute a function before committing.- Conventional Commits by default.
- Ships ESM and CJS bundles.
- Supports config file
bump.config.ts:
// bump.config.ts
import { defineConfig } from 'bumpp'
export default defineConfig({
// ...options
})Template tokens
The commit message, tag name, and pull request branch/title/body all support named tokens. This is the recommended template style:
| Token | Description | Example |
| --------------- | -------------------------------------------------- | ------------ |
| {version} | The new version number | 1.2.3 |
| {oldVersion} | The previous version number | 1.2.2 |
| {tag} | The formatted tag name | v1.2.3 |
| {releaseType} | The release type (empty for explicit versions) | patch |
| {major} | The major segment of the new version | 1 |
| {minor} | The minor segment of the new version | 2 |
| {patch} | The patch segment of the new version | 3 |
| {date} | The current date (YYYY-MM-DD, local time) | 2026-07-28 |
bumpp --commit "chore: release {tag}" --tag "{version}"The legacy
%splaceholder (replaced with the new version) still works but is soft-deprecated in favour of{version}. If a template contains any named token,%ssubstitution is disabled for that template.
Releasing via a Pull Request
Pushing version-bump commits and tags straight to main is convenient for solo
projects, but on a team it bypasses branch protection and code review. The
--pr flag lets you drive releases through a pull request instead: a maintainer
approves and merges it, and CI tags the merge commit and publishes.
bumpp --prWith --pr, bumpp:
- Checks the working tree is clean, that you are on the base branch (the remote
default branch, detected via
origin/HEAD), and that it is not behind its remote. - Creates a release branch (
release/v{version}by default). - Bumps the version in your files and runs the
executescript, if any. - Commits the bump — no tag is created locally; the tag is created by CI after the pull request is merged.
- Pushes the branch, then switches you back to your original branch.
- Offers to open a pull request via the local
ghCLI (auto-created with--yes). Ifghis unavailable, it prints a link to open the pull request manually.
If an open release pull request for the same version already exists on the same
release branch and was opened by you, bumpp updates it instead of creating a
duplicate — the branch is force-pushed and the pull request's title and body are
refreshed. Re-run bumpp --pr and pick the same version to amend an in-progress
release PR.
The release/ branch prefix is the marker CI uses to recognise a release pull
request, so keep it unless you also update your workflow.
Configuration
--pr toggles the feature on the CLI. For finer control, use the object form in
bump.config.ts:
// bump.config.ts
import { defineConfig } from 'bumpp'
export default defineConfig({
pr: {
branch: 'release/v{version}', // release branch name template
base: 'main', // PR base branch (defaults to origin/HEAD)
title: 'chore: release {tag}', // defaults to the release commit message
body: '{oldVersion} → {version}', // template string, or a function receiving the tokens
draft: false, // open the PR as a draft
},
})pr requires push to be enabled, and implies that no tag is created locally.
GitHub Actions setup
Because a tag pushed with the default GITHUB_TOKEN will not trigger another
workflow (GitHub's recursion guard), the release runs in a single workflow that
reacts to the release pull request being merged, creates the tag, and publishes:
# .github/workflows/release-pr.yml
name: Release (PR merged)
on:
pull_request:
types: [closed]
# Only run for merged release/* pull requests that originate from THIS repo.
# The `head.repo.full_name == github.repository` check is essential: it stops a
# fork from opening a PR whose branch is named `release/*` and having this
# privileged workflow run against it. See "Security" below.
jobs:
release:
if: >-
github.event.pull_request.merged == true &&
github.event.pull_request.head.repo.full_name == github.repository &&
startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest
permissions:
contents: write # create the tag, release, and read the repo
id-token: write # npm OIDC trusted publishing
steps:
- uses: actions/checkout@v5
with:
# Check out the merge commit so package.json has the bumped version
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0
- uses: actions/setup-node@v5
with:
node-version: 22
registry-url: https://registry.npmjs.org
- name: Read version
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
- name: Create tag
uses: actions/github-script@v8
with:
script: |
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/v${{ steps.version.outputs.version }}`,
sha: context.payload.pull_request.merge_commit_sha,
})
- run: npm ci
- run: npm run build --if-present
# Generate the GitHub Release notes from conventional commits
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Publish with npm OIDC trusted publishing (no NPM_TOKEN needed).
# Requires npm CLI >= 11.5.1 and a configured trusted publisher on npmjs.com.
- run: npm publishThen protect main (Settings → Branches): require a pull request and at least
one approval before merging. Now the only way to cut a release is bumpp --pr →
review → merge, and CI does the rest.
If you already have a workflow triggered by
v*tags, you can instead push the tag from a merge-triggered job using a Personal Access Token or a GitHub App token (notGITHUB_TOKEN) so the tag push triggers your existing pipeline.
Security
This workflow publishes to npm, so it must never run untrusted code with privileged credentials. The example is designed to be safe:
- It uses
pull_request, notpull_request_target. The workflow definition is always read from your default branch, so a pull request cannot modify the release logic. (pull_request_targetwould run with a read/write token and your secrets even for forks — never use it here.) - It requires the release branch to live in your repository
(
head.repo.full_name == github.repository). Therelease/*branch name is attacker-controllable from a fork, so the name check alone is not enough — a forker could open a PR from a branch calledrelease/v9.9.9. The same-repo guard ensures the job never even checks out or runs fork-authored code (andbumpp --pralways pushes the release branch to your repo, so legitimate releases are unaffected). As an additional backstop, GitHub gives fork pull requests a read-onlyGITHUB_TOKENwith no secrets or OIDC, so the tag and publish steps would fail rather than leak anyway. - Protect
mainwith required reviews so a release can only be merged by a maintainer.
For extra defense-in-depth, run the publish step in a dedicated GitHub Environment with required reviewers, so npm publishing needs an explicit second approval:
jobs:
release:
environment: release # npm publish now needs a second approval
# ...rest of the job as above