myopensoft / laravel-version-bumper
Bump your app's semantic version from Conventional Commits and generate a Keep-a-Changelog entry.
Package info
gitlab.com/myopensoft/laravel-version-bumper
pkg:composer/myopensoft/laravel-version-bumper
Requires
- php: ^8.3
- illuminate/console: ^11.0||^12.0||^13.0
- illuminate/contracts: ^11.0||^12.0||^13.0
- illuminate/support: ^11.0||^12.0||^13.0
- myopensoft/php-version-bumper: ^1.1
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8||^9.0
- orchestra/testbench: ^9.0||^10.0||^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-phpunit: ^2.0
- vimeo/psalm: ^6.0
README
Bump your Laravel app's semantic version from Conventional Commits and generate a Keep a Changelog entry — from the commits since your last git tag.
The version logic — Conventional Commit parsing, bump detection, changelog rendering, and the git work — lives in myopensoft/php-version-bumper, which has no dependencies and can run under plain PHP. This package is the Laravel adapter: a service provider, an Artisan command, and a publishable config file.
Installation
composer require myopensoft/laravel-version-bumper --dev
Publish the config (optional):
php artisan vendor:publish --tag="version-bumper-config"
Usage
# Preview only — nothing is written
php artisan version:bump --dry-run
# Write the new version + changelog (no git actions)
php artisan version:bump
# Write, then commit / tag / push (each is opt-in)
php artisan version:bump --commit --tag --push
The bump level follows Conventional Commits: feat → minor, fix/perf → patch, and any
commit marked breaking (feat!: or a BREAKING CHANGE: footer) → major. The baseline is your
most recent git tag; if no commit since that tag warrants a release, the command exits quietly.
Worked example
Given a repo tagged 1.2.0 with these commits since the tag:
feat(billing): add invoice export
fix(api): correct pagination offset
docs: tweak readme
feat outranks fix, so the next version is a minor bump — and docs is ignored:
$ php artisan version:bump --dry-run
INFO Bumping 1.2.0 → 1.3.0 (Minor).
## [1.3.0] — 2026-06-14
### Added
- billing: add invoice export
### Fixed
- api: correct pagination offset
WARN Dry run — nothing written, committed, or pushed.
Dropping --dry-run writes VERSION and prepends the entry to CHANGELOG.md. Adding
--commit --tag then creates the release commit and tag:
$ php artisan version:bump --commit --tag
INFO Bumping 1.2.0 → 1.3.0 (Minor).
...
INFO Released 1.3.0.
$ git log --oneline -1 && git tag --list '1.3.0'
0943494 chore(release): 1.3.0
1.3.0
Configuration
config/version-bumper.php lets you choose where the version lives (source):
file(default) — a plainVERSIONfile (version_file).config_file— regex-patch a literal in any file, e.g.'version'inconfig/app.php.git_tag— read from the latest tag; the release tag is the source of truth (no file written).
You can also customise the changelog path, date format, the version shape (scheme), the
commit-type → changelog-section map (sections), the commit-type → bump-level map (bumps),
and the git remote/branch.
Version scheme (four-segment versions)
By default the version is three-segment semver. Set scheme to change its shape — the
template reads like the version it produces, so the number of dots is the number of segments:
// config/version-bumper.php
'scheme' => 'manual.manual.minor.patch',
That gives you a four-segment 1.2.3.4 where you own 1.2 and the command owns 3.4:
scheme | version | fix: | feat: | feat!: |
|---|---|---|---|---|
major.minor.patch (default) | 1.2.3 | 1.2.4 | 1.3.0 | 2.0.0 |
manual.manual.minor.patch | 1.2.3.4 | 1.2.3.5 | 1.2.4.0 | 1.2.4.0 |
major.minor | 1.2 | 1.3 | 1.3 | 2.0 |
Tokens are major, minor, patch, and manual. Each level token may appear at most once;
manual may repeat. Segments to the left of the bumped one are copied through unchanged; the
bumped segment and everything to its right is rewritten. A manual segment is only left alone
when it sits to the left of the bumped one — a manual segment to the right is zeroed like any
other, so it is not a place to keep a hand-maintained build number.
When a bump level has no segment of its own it falls back to the nearest less significant
mapped level, and only failing that to the nearest more significant one. So under
manual.manual.minor.patch a breaking change bumps the minor segment rather than blocking the
release — the command warns, and the break still appears in the changelog:
$ php artisan version:bump --dry-run
WARN Breaking change found, but this scheme has no 'major' segment — bumping the minor segment instead.
INFO Bumping 1.2.3.4 → 1.2.4.0 (Minor).
Switching schemes needs no re-tagging: a baseline tag with the wrong number of segments is zero-padded or truncated to fit, with one warning per run.
WARN Baseline 1.2.3 read as 1.2.3.0 to match the 4-segment scheme.
INFO Bumping 1.2.3.0 → 1.2.4.0 (Minor).
An invalid template — an unknown token, an empty segment, a repeated level, or an all-manual
scheme — throws an InvalidArgumentException when the container resolves the binding.
Which commits trigger a release
By default only feat, fix, and perf commits (plus any breaking change) trigger a release;
everything else (docs, chore, test, refactor, style, …) is ignored. Add an entry to the
bumps map if you want another type to trigger a bump, and to sections if you want it to appear
in the changelog.
Git actions are opt-in
A plain php artisan version:bump only writes the version and changelog. Committing, tagging, and
pushing each require their own flag (--commit, --tag, --push), so the package never touches
your git remote unless you ask it to.
Testing & static analysis
composer test # Pest
composer analyse # PHPStan level 10
composer psalm # Psalm errorLevel 1
composer format # Pint
License
The MIT License (MIT). Please see the License File for more information.