semantic-release
Automated versioning and package publishing
semantic-release is an open-source tool that automates version management and package release based on commit messages. It runs in self-hosted CI pipelines to determine versions and publish artifacts.
Key features
- Commit-driven versioning
- Automated changelog
- Plugin ecosystem
- CI integration
Pros & cons
Strengths
- Fully automated releases
- Rich plugin system
Trade-offs
- Strict commit conventions
- Node.js required
- CI setup involved
semantic-release replaces
Last reviewed Aug 26, 2026 · 763 words
semantic-release never asks you what the next version is. It reads the commits since the last tag, decides patch, minor, or major from their messages, tags the release, writes the notes, publishes the artifact, and refuses to run at all if it is not inside a CI job. That last part surprises people: run it on a laptop and it exits with a polite message about not detecting CI, because the whole point is that humans stop doing releases by hand. It is MIT, Node.js, 24,004 stars, and 12 years old, and it belongs in the stack of anyone running a self-hosted forge who is tired of git tag v1.4.2 followed by a changelog written from memory.
Conventional Commits are the rule, not a suggestion
The default analyser uses the Angular convention: a commit starting fix: bumps the patch, feat: bumps the minor, and a BREAKING CHANGE: footer or a ! after the type bumps the major. Anything else (chore:, docs:, refactor:) produces no release. This is strict on purpose and it is the con the catalogue lists first. Enforce it at the door with commitlint in a pre-commit hook or a CI check, and squash-merge pull requests so the merge commit is the one that carries the type. Teams that skip the enforcement end up with a main branch that never releases because nobody typed feat:.
The default plugins assume GitHub and npm
Out of the box semantic-release loads four plugins: commit analyser, release-notes generator, npm publisher, and GitHub release creator. For a self-hosted forge you swap the last two. The official @semantic-release/gitlab plugin talks to GitLab CE with a GITLAB_TOKEN and GITLAB_URL. For Gitea and Forgejo there is no first-party plugin; a community one exists, and the pattern I trust more is @semantic-release/git to push the tag and changelog plus @semantic-release/exec running a curl against the forge's release API. A minimal .releaserc.json for a non-npm project on GitLab:
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
["@semantic-release/git", { "assets": ["CHANGELOG.md"] }],
"@semantic-release/gitlab"
]
}
The @semantic-release/npm plugin is absent because most self-hosted projects are not npm packages; leaving it in with no registry token is the most common failure on a first run.
The CI job is the install
Because it only runs in CI, the setup is a job on your forge's runner: check out with full history (a shallow clone cannot see the last tag), install Node 20 or newer, run npx semantic-release. Give the job a token with write access to the repository, because it pushes a tag and, with the git plugin, a commit. A Woodpecker or Gitea Actions step is about 6 lines; the CI/CD category covers which runner pairs with which forge. Test with npx semantic-release --dry-run from the CI job first, which prints the version it would cut without touching anything.
Branch strategy is where the power hides
The branches array supports maintenance lines (1.x) that only receive patches, and pre-release branches (beta, next) that produce 2.0.0-beta.3 style tags. That is what lets a small project ship a stable and a beta channel from one config with no manual bookkeeping. It is also the feature that makes the docs long, and if you only ship from main, ignore it.
Where release-please differs
release-please, Google's alternative, opens a "release PR" that accumulates the changelog and only tags when you merge it, which gives a human a last look. semantic-release tags the moment a feat: lands. If your team wants to batch releases, release-please; if every merge to main should be deployable and versioned immediately, semantic-release. I use the latter for libraries and Docker images and the former for anything with an announcement attached.
What I'd do
Add commitlint first and live with it for 2 weeks before adding semantic-release, because the tool is only as good as the commit messages. Then the config above, a CI job with full clone depth and a write token, a dry run, and one real release on a throwaway tag. On a self-hosted forge, put the tag-and-changelog step through the git plugin so the record lives in the repository rather than in the forge's release UI; that way a forge migration later loses nothing.
Similar developer tools & git apps
Excalidraw
Developer Tools & GitVirtual hand-drawn style whiteboard
Replaces Miro
lazygit
Developer Tools & GitSimple terminal UI for Git commands
Replaces GitKraken, Sourcetree
Hoppscotch
Developer Tools & GitOpen-source API development ecosystem
Replaces Postman, Insomnia
json-server
Developer Tools & GitFull fake REST API from a JSON file in seconds
Replaces Mockoon, Postman Mock
Strapi
Developer Tools & GitLeading open-source headless CMS
Replaces Contentful
NocoDB
Developer Tools & GitOpen-source Airtable alternative
Replaces Airtable