Skip to main content

Semantic Versioning

How versioning works

Versioning is automated through semantic-release, a utility that analyzes commit messages during the CI pipeline and determines the next version number based on Semantic Versioning: <major>.<minor>.<patch>.

The plugin @semantic-release/commit-analyzer inspects commit headers to infer the release type. Each commit must include a type prefix (e.g., feat:, fix:) to be correctly interpreted.

Commit types and their impact

Major releases

  • Triggered by BREAKING CHANGE: in the commit body.

Minor releases

  • Triggered by feat: in the commit body.

Patch releases

  • Triggered by:
    • fix: (bug fixes)
    • docs: (documentation changes)
    • refactor: , perf: , test: , ci: , build: (various small improvements and tooling updates)

The @semantic-release/git plugin updates the version field in package.json, commits release assets, and creates a tag for the new version:

{
"version": "<major>.<minor>.<patch>"
}

Handling multiple commits

When multiple commits are present in a release, the highest-impact change determines the release type. Examples:

  • If the previous version was 1.2.0 and you commit feat: new feature and fix: minor bug, the next version will be 1.3.0 (minor).
  • If you commit two fix: changes, it results in a patch bump: 1.3.1.

Configuring semantic-release

Semantic-release requires proper setup in your CI pipeline, package.json, and .releaserc.json.

CI configuration

The publish task must run on the default branch, without being tied to a tag (since semantic-release generates the tag itself):

publish:
stage: release
script:
- npm run build
- npm run semantic-release
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

package.json

Ensure the correct scripts and dev dependencies are defined:

{
"scripts": {
"semantic-release": "semantic-release"
},
"devDependencies": {
"@semantic-release/git": "10.0.1",
"semantic-release": "19.0.2"
}
}

.releaserc.json

This file defines plugins and release rules. It tells semantic-release how to interpret commit types and which files to include in the release commit:

{
"branches": ["master"],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{ "breaking": true, "release": "major" },
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "docs", "release": "patch" },
{ "type": "refactor", "release": "patch" },
{ "type": "style", "release": "patch" },
{ "type": "build", "release": "patch" },
{ "type": "ci", "release": "patch" },
{ "type": "test", "release": "patch" },
{ "type": "perf", "release": "patch" }
],
"parserOpts": {
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES"]
}
}
],
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": ["package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}

Supporting tools for commits

To simplify writing properly formatted commits, consider using:

  • commitizer: Interactive CLI for writing commits
  • commitlint: Lints commit messages based on conventional rules.

These tools help enforce consistency and ensure semantic-release functions as expected.

Further Reading