
September 09, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Manual version bumps waste time and break trust. One developer tags v2.3.1 while another ships 2.4.0 without a changelog entry. Semantic Release Automated Versioning fixes that by reading your commit history and letting CI publish the next semver tag, release notes, and package artefact. If you already run automated checks in CI, this is the natural next step for libraries, CLI tools, and frontend packages your team ships repeatedly.
What is Semantic Release Automated Versioning and how does it work?
Semantic Release is an npm CLI that automates the release workflow for Node.js packages. It does not guess versions from file diffs. It parses commit messages against the Conventional Commits specification and applies Semantic Versioning rules to decide whether the next release is a patch, minor, or major increment.
The tool runs only on protected branches—typically main or master. Feature branches get no release. That design keeps nightly experiments from publishing broken packages to npm.
On a real client project, I treat Semantic Release Automated Versioning as a contract between developers and CI. Developers write structured commits. CI owns tags, changelogs, and registry uploads. Nobody edits package.json version fields by hand on release day.
The default plugin chain handles most teams without custom code:
- @semantic-release/commit-analyzer — reads commits since the last tag and picks the semver level.
- @semantic-release/release-notes-generator — builds human-readable release notes.
- @semantic-release/changelog — commits an updated
CHANGELOG.mdback to the repo. - @semantic-release/git — commits version bumps in tracked files like
package.json. - @semantic-release/npm — publishes to the npm registry.
- @semantic-release/github or gitlab — creates a release on your forge.
Laravel and PHP projects do not use semantic-release for the framework itself. You can still adopt the same commit discipline for shared npm assets—Vite 8.x frontends, design tokens, or internal CLI tools. Your PHP app versioning stays separate, often tied to API route versioning rather than npm semver.
How do you set up semantic-release in a Node.js CI pipeline?
Start with a package that actually publishes to npm or GitHub Releases. Internal Laravel apps deployed via Deployer rarely need semantic-release unless you extract a reusable library.
Install dependencies
Use Node.js 26 LTS and npm 12 on your CI runner. Pin them in your pipeline config so local and CI environments match.
npm install --save-dev semantic-release \
@semantic-release/changelog \
@semantic-release/git \
@semantic-release/github Create the release config
Add release.config.js at the project root:
/** @type {import('semantic-release').GlobalConfig} */
export default {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
['@semantic-release/changelog', { changelogFile: 'CHANGELOG.md' }],
'@semantic-release/npm',
['@semantic-release/git', {
assets: ['CHANGELOG.md', 'package.json', 'package-lock.json'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
}],
'@semantic-release/github'
]
}; The [skip ci] token in the release commit message prevents infinite CI loops. GitLab CI and GitHub Actions both honour common skip patterns when configured correctly.
Wire GitLab CI
I've used this pattern on sister sites sharing a Deployer 7 + GitLab CI pipeline. The release job runs only after lint and test jobs succeed on main:
stages:
- test
- release
test:
stage: test
image: node:26
script:
- npm ci
- npm test
release:
stage: release
image: node:26
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- npm ci
- npx semantic-release
variables:
GITLAB_TOKEN: $GL_TOKEN
NPM_TOKEN: $NPM_TOKEN Store GL_TOKEN and NPM_TOKEN as masked CI variables. The GitLab token needs write_repository scope so semantic-release can push tags and changelog commits. The npm token needs publish rights for your scoped package.
Verify locally before merging
Run a dry run to preview the next version without publishing:
npx semantic-release --dry-run --no-ci The dry-run output lists which commits trigger a release and what version number CI would assign. Fix commit message formatting before merging if the analyser reports no releasable commits when you expected one.
What commit message format does semantic-release require?
Semantic-release depends entirely on commit message structure. A free-form message like "fixed login bug" produces no release even when the code change is critical. Train your team on Conventional Commits before enabling automated publishing.
Valid prefixes include feat:, fix:, perf:, refactor:, docs:, chore:, and test:. Only certain types trigger version bumps. Documentation and chore commits update the repo but skip releases unless they include a breaking-change footer.
Examples that semantic-release understands:
feat(auth): add OAuth2 password grant support
fix(cart): prevent double-charge on retry
feat(api)!: remove v1 endpoints
BREAKING CHANGE: v1 routes deleted; migrate to /api/v2 The exclamation mark after the type scope (feat(api)!:) is shorthand for a breaking change. The explicit BREAKING CHANGE: footer works inside any commit type and always forces a major bump.
Enforce format at commit time with commitlint and Husky hooks. Without enforcement, one sloppy merge commit blocks an expected release and nobody knows why until CI logs show "no releasable commits."
For mixed stacks—Laravel 13.x backend plus a Vite frontend—keep commit scopes clear. Use feat(ui): for frontend features and fix(api): for backend fixes if both live in one monorepo with shared semantic-release config.
How does semantic-release compare to manual versioning?
Teams without automation rely on a release manager to bump package.json, write changelog entries, create Git tags, and run npm publish. That works for quarterly releases. It falls apart when you ship weekly fixes to a shared internal library used across multiple client projects.
| Criteria | Manual versioning | Semantic Release Automated Versioning |
|---|---|---|
| Version accuracy | Depends on human judgment; easy to ship minor changes as patches | Deterministic rules from commit types; same input always yields same semver |
| Changelog quality | Often stale, copied from memory, or skipped entirely | Generated from commit messages on every release |
| Release speed | Blocks on one person with publish credentials | Runs automatically after CI green on main |
| Audit trail | Tags may not match changelog entries | Tag, changelog commit, and registry version stay aligned |
| Setup cost | Zero tooling; high ongoing labour | One-time CI and commitlint setup; low ongoing cost |
| Best fit | Private apps deployed via SSH/Deployer with no npm package | Published npm packages, shared UI libraries, open-source tools |
On production Laravel applications I deploy with Deployer 7, version numbers often live in environment config rather than npm. Semantic-release shines when you maintain a package other teams install—not when you rsync a Blade app to an Ubuntu server. Match the tool to the artefact.
If you need help wiring CI for a mixed PHP and Node stack, support and maintenance services often cover pipeline hardening alongside application fixes.
What are common semantic-release mistakes in production CI/CD?
Most failures I see are configuration problems, not bugs in semantic-release itself. The tool fails loudly when credentials or branch rules are wrong. Learn these patterns before your first production release.
Infinite CI loops
When semantic-release commits a changelog back to main, that push retriggers CI. Without [skip ci] in the release commit message, your pipeline runs release twice. Worse, a misconfigured loop burns runner minutes every night.
Missing npm provenance or 2FA
npm requires automation tokens for CI publishes when 2FA is enabled on the account. Legacy passwords fail silently in some setups. Create a granular access token with publish-only scope and store it as a masked variable.
Squash merges hide commit history
GitHub squash merges collapse PR commits into one message. If the squash title ignores Conventional Commits format, semantic-release sees one useless commit like "Update stuff" and skips the release. Enforce PR title format or rebase-merge instead.
Monorepo complexity without a monorepo plugin
Standard semantic-release assumes one package at the repo root. Multiple packages need @semantic-release/monorepo or independent per-package configs. I've seen teams publish the wrong package version because paths were not scoped correctly.
Expecting PHP Composer releases
Semantic-release targets npm by default. PHP libraries on Packagist need different tooling—often manual tags or a custom CI script. Do not force semantic-release onto a Composer-only repo without the community PHP plugins and clear justification.
Validate your release config JSON with the JSON formatter tool before committing plugin arrays. A trailing comma in a copied config breaks the release job at the worst moment.
Advanced: monorepos and pre-releases
Use the branches array for beta and alpha channels:
branches: [
'main',
{ name: 'beta', prerelease: true },
{ name: 'alpha', prerelease: 'alpha' }
] Commits on beta publish versions like 1.2.0-beta.1. Consumers opt in via npm dist-tags. This pattern suits shared component libraries shipped alongside e-commerce frontends where staging needs npm packages before production promotion.
For API versioning at the HTTP layer—not npm semver—read API versioning strategies compared. Semantic-release and URL-based API versions solve different problems and often coexist in the same organisation.
Key Takeaways
- Semantic Release Automated Versioning converts Conventional Commits into semver tags, changelogs, and npm publishes inside CI—no manual
package.jsonedits. - Install semantic-release with changelog, git, and npm plugins; run it only on protected
mainafter tests pass. - Enforce commit format with commitlint—squash merges and vague PR titles are the top reason releases silently skip.
- Use dry-run mode locally to preview the next version before merging breaking changes.
- Reserve semantic-release for published npm packages; Deployer-managed Laravel apps need different release mechanics.
- Store NPM_TOKEN and forge tokens as masked CI variables and include
[skip ci]in release commit messages to prevent pipeline loops.
People Also Ask
Does semantic-release work with GitHub Actions?
Yes. Replace GitLab variables with GITHUB_TOKEN or a personal access token with repo scope. The official @semantic-release/github plugin creates releases and uploads assets. The commit-analyzer and npm plugins work identically across CI platforms.
Can semantic-release version private packages?
It publishes to private npm registries the same way as public ones. Provide an automation token with publish rights to your scoped package. Self-hosted GitLab npm registries and GitHub Packages both integrate through the npm plugin with adjusted registry URLs.
What happens if no commits warrant a release?
CI exits with code zero and logs "no releasable commits." No tag is created and nothing publishes to npm. This is normal when a sprint only includes documentation and chore commits.
Is semantic-release the same as conventional-changelog?
Related but different. conventional-changelog generates changelogs from commits without publishing. semantic-release orchestrates the full release—including version calculation, git tags, registry publish, and forge releases—in one automated pipeline.
Ship reliable releases without manual version drift
Semantic Release Automated Versioning pays off when your team ships shared packages weekly and needs every tag to match a changelog entry and npm artefact. Start with commitlint, add a dry-run job to CI, then enable publishing on main once credentials and branch protection are solid. The setup takes an afternoon; the saved release meetings last for years.
If you want help connecting semantic-release to a broader GitLab CI pipeline—or separating npm library versioning from your Laravel deploy workflow—reach out for a consultation. You can also review how similar automation was applied on the Adventure Third Pole Trek booking platform or explore custom software development for mixed PHP and Node stacks. For related reading, see PHP Rector for automated refactoring, automated server backups, and model versioning and registries. Visit the home page, about page, services overview, portfolio, blog, or Linux system administration for more resources. Need API work alongside frontend packages? See API development services and enterprise application development.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

