Interactive CLI that bumps your version numbers and more
960
stars
618
commits
TypeScript
primary language
Sep 3, 2026
updated
Forked from version-bump-prompt
bumpp - so you can use npx bumpp directly.preid when available.--commit --tag --push by default. (opt-out by --no-push, etc.)-r or --recursive to bump all packages in the monorepo.--execute to execute the command, or execute a function before committing.bump.config.ts:// bump.config.ts
import { defineConfig } from 'bumpp'
export default defineConfig({
// ...options
})
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.
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 --pr
With --pr, bumpp:
origin/HEAD), and that it is not behind its
remote.release/v{version} by default).execute script, if any.gh
CLI (auto-created with --yes). If gh is 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.
--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.
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 publish
Then 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.
This workflow publishes to npm, so it must never run untrusted code with privileged credentials. The example is designed to be safe:
pull_request, not pull_request_target. The workflow definition
is always read from your default branch, so a pull request cannot modify the
release logic. (pull_request_target would run with a read/write token and
your secrets even for forks — never use it here.)head.repo.full_name == github.repository). The release/* 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 called release/v9.9.9. The same-repo
guard ensures the job never even checks out or runs fork-authored code (and
bumpp --pr always pushes the release branch to your repo, so legitimate
releases are unaffected). As an additional backstop, GitHub gives fork pull
requests a read-only GITHUB_TOKEN with no secrets or OIDC, so the tag and
publish steps would fail rather than leak anyway.main with 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
(top 30 of 47)
TypeScript
99.8%
Interactive CLI that bumps your version numbers and more
960
stars
618
commits
TypeScript
primary language
Sep 3, 2026
updated
Forked from version-bump-prompt
bumpp - so you can use npx bumpp directly.preid when available.--commit --tag --push by default. (opt-out by --no-push, etc.)-r or --recursive to bump all packages in the monorepo.--execute to execute the command, or execute a function before committing.bump.config.ts:// bump.config.ts
import { defineConfig } from 'bumpp'
export default defineConfig({
// ...options
})
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.
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 --pr
With --pr, bumpp:
origin/HEAD), and that it is not behind its
remote.release/v{version} by default).execute script, if any.gh
CLI (auto-created with --yes). If gh is 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.
--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.
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 publish
Then 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.
This workflow publishes to npm, so it must never run untrusted code with privileged credentials. The example is designed to be safe:
pull_request, not pull_request_target. The workflow definition
is always read from your default branch, so a pull request cannot modify the
release logic. (pull_request_target would run with a read/write token and
your secrets even for forks — never use it here.)head.repo.full_name == github.repository). The release/* 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 called release/v9.9.9. The same-repo
guard ensures the job never even checks out or runs fork-authored code (and
bumpp --pr always pushes the release branch to your repo, so legitimate
releases are unaffected). As an additional backstop, GitHub gives fork pull
requests a read-only GITHUB_TOKEN with no secrets or OIDC, so the tag and
publish steps would fail rather than leak anyway.main with 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
(top 30 of 47)
TypeScript
99.8%