marcocesarato/php-conventional-changelog
Automatically generate changelogs and release notes from your Git commit history using Conventional Commits and SemVer. Configurable, Composer-friendly, and easy to run from the terminal for streamlined releases.
Architecture fit. This is not a Laravel package in the framework sense — there is no service provider, no publishable config, no Artisan command, no facade. It is a standalone Symfony Console CLI binary (vendor/bin/conventional-changelog) that reads the local Git history, parses commit messages against conventionalcommits.org, computes a SemVer bump, writes CHANGELOG.md, and optionally commits and tags. For a Laravel codebase that is actually a reasonable shape: it sits entirely in the require-dev layer, never loads during a request, and has no runtime coupling to the application container. The integration surface is composer.json scripts plus a .changelog config file at repo root — both of which are already familiar artifacts to a Laravel team. The main architectural mismatch is that Laravel projects typically version via git tags and config/app.php is irrelevant here; any "version" the tool bumps lives in Git metadata and (possibly) composer.json, which means the source of truth for versioning must be agreed on before adoption.
Integration feasibility. High, with caveats. Install is one dev dependency; wiring it up is a handful of Composer scripts (changelog, release, release:patch|minor|major). The tool has been actively maintained through Symfony 7 and Symfony 8 compatibility (1.17.1, 1.17.3), which matters because Laravel 11/12 pin symfony/console majors — a stale package here would produce Composer constraint conflicts with the framework itself. PHP floor of 7.1.3 is comfortably below any modern Laravel requirement, so no conflict there. The friction points are process, not code: it only works if the team's commit messages are actually conventional, and it needs full tag history in the checkout (shallow CI clones will produce wrong version bases).
Technical risk. Four things I would flag to an architecture review:
composer.json on a commercial product.--commit, --amend, --commit-all, and --no-verify let a single command mutate Git state, create tags, and bypass hooks. In a CI context with a write-scoped token, a misconfiguration can push tags or commit unintended staged files.Key questions for the maintainer/spike:
composer.json, or is version state purely tag-derived? This determines whether it conflicts with our existing versioning approach.fetch-depth: 1 is the GitLab/GitHub default)?ignoreTypes list drops perf, refactor, and revert. Is that acceptable for release notes our customers read?path argument) for a monorepo with multiple Laravel packages?Stack fit. Good fit for a single-repo Laravel application on PHP 8.1+ with Git-flow or trunk-based development. Poor fit for a monorepo unless the positional path argument cleanly isolates per-package changelogs — validate this before standardizing. It coexists fine with Pint, PHPStan, Pest/PHPUnit, and CaptainHook; none of those overlap functionally.
Migration path. Assume the team currently either hand-writes CHANGELOG.md, uses release-please/semantic-release in GitHub Actions, or has no changelog at all. The migration is not a code migration — it is a commit-hygiene migration. Sequence it as: (1) enforce conventional commits first via a commit-msg hook or commitlint in CI, (2) then introduce the generator. Doing it in the other direction produces a first run full of unparseable commits and a changelog nobody trusts. If the team is coming off semantic-release (Node), note that this package covers the changelog and tag portion but not the npm publish/artifact step, so remove the old tool only after confirming parity on the release-notes format stakeholders read.
Compatibility. Verify symfony/console constraint resolution against the target Laravel major before committing. On Laravel 10 (symfony/console ^6) and Laravel 11/12 (^7), the 1.17.x line should resolve; older 1.16.x will not on Laravel 11+. Pin a minor version rather than floating, since a dev-tool major bump breaking the release pipeline is a bad way to start a Tuesday.
Sequencing.
--first-release into a scratch branch, diff the output against the existing changelog. ~half a day..changelog config: decide the type allowlist, whether perf/refactor/revert should appear, and the section headings.fetch-depth: 0, a scoped token, and --commit --annotate-tag (annotated tags are preferable to lightweight for auditability; --sign-tag only if GPG is already in the org's release practice).--no-verify in CI, never --commit-all (it sweeps staged files unrelated to the release), and reserve --history for a one-time backfill because it overwrites CHANGELOG.md.Maintenance. Low and contained. It lives in require-dev, never ships to production, and its failure mode is "no changelog this release," not "site down." Realistic maintenance is a quarterly dependency-bump review plus a re-run of the spike whenever the Symfony Console major moves. Budget roughly one engineer-day per year, plus whatever time the team spends fixing non-conventional commit messages in review.
Support. Single-maintainer project with no commercial backing and no vendor SLA. There is no escalation path for a broken release. Mitigation: keep the release step as a discrete CI job so a failure degrades to "release without auto-changelog" rather than blocking deploys, and document a manual fallback (hand-written release notes, or swap to git-cliff/git-cliff-style Rust binary or release-please) that can be executed in under an hour.
Scaling. Fine for a single application repo with a few hundred to a few thousand commits. Two scaling limits: monorepos (one changelog per invocation; multi-package repos need per-package runs and careful path handling), and --history regeneration, which reads the entire commit graph and rewrites the file — acceptable as a backfill, not as a per-release step. Large histories with thousands of tags may make date/tag-range resolution slow but not pathological.
Failure modes to instrument for.
fetch-depth: 0 and assert tags exist before running.git push --follow-tags step, verified.--no-verify bypassing pre-commit hooks → unreviewed file committed to the release. Fix: policy, not tooling.Ramp-up. Very low. A developer who has used any CLI release tool is productive in under an hour: --help is comprehensive, the config file is a PHP array with documented keys, and there are worked examples for first release, history, date range, tag range, and prerelease channels. The non-obvious knowledge is entirely about consequences, not syntax — which flags mutate Git state, which overwrite files, and which bypass safety checks. That belongs in a short internal runbook rather than being discovered by a release engineer at 6pm on a Friday.
How can I help you explore Laravel packages today?