Automated Multi-Platform Releases with semantic-release
Semantic-release is a popular tool that fully automates versioning and publishing your project based on commit messages. By following a Conventional Commits style (e.g. feat:
, fix:
prefixes), it determines whether to bump a major, minor or patch version and publishes artifacts accordingly. You can configure it to publish to npm, create GitHub Releases, and even tag & push Docker images - all without manual intervention.
1. Setup & Installation
First, install semantic-release and the plugins you need:
npm install --save-dev semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator @semantic-release/npm @semantic-release/github
If you want Docker image publishing, also install a Docker plugin like felixfbecker/semantic-release-docker or @codedependant/semantic-release-docker.
Next, ensure your project uses Conventional Commits: semantic-release will parse your commit messages to infer the next version. Tools like commitlint or commitizen can help enforce this format. Initialize a Git repo (if you haven’t already) and make sure the latest release tag is present (see Troubleshooting below).
Finally, define a script to run releases. In package.json
add:
"scripts": {
"release": "semantic-release"
}
This lets you invoke semantic-release via npm run release
(typically from CI).
2. Configuring semantic-release
Semantic-release can be configured in a release.config.js
(or .releaserc
) file or in the release
key of package.json
. Here’s a simple example release.config.js
for a Node project:
// release.config.js
module.exports = {
branches: ['main'], // release from main branch
repositoryUrl: 'https://github.com/your-org/your-repo.git',
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/npm',
'@semantic-release/github'
]
};
This config tells semantic-release to run on the main
branch and use four plugins:
- Commit Analyzer (to parse commit messages)
- Release Notes Generator (to build CHANGELOG.md entries)
- npm (to publish to npm)
- GitHub (to create a GitHub Release)
You could also put this in a .releaserc
JSON file. For instance, a minimal JSON config looks like:
// .releaserc
{
"branches": ["main"],
"repositoryUrl": "https://github.com/your-org/your-repo.git",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/npm",
"@semantic-release/github"
]
}
(config examples are adapted from the official docs.)
3. Publish to npm
To publish your package to npm, include the @semantic-release/npm
plugin in your config (as shown above). This plugin will update the version
in package.json
and run npm publish
for you. It requires an NPM_TOKEN
with publishing rights in the CI environment. In practice, you should:
- Set
NPM_TOKEN
in CI: Store an npm automation token (see npm docs) in your CI system (e.g. GitHub Secrets). Semantic-release’s verify step will check for this token. - Ensure
publishConfig
if needed: You can also set"publishConfig": { "registry": "...", "tag": "latest" }
inpackage.json
to control the npm registry and dist-tag.
In your config, you’ll have something like:
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/npm",
"@semantic-release/github"
]
With this, when semantic-release runs it will bump the package version (in prepare
), publish the package (in publish
), and tag it. The npm plugin’s own docs outline the steps:
Step | Description |
---|---|
verifyConditions | Checks that NPM_TOKEN is set (or .npmrc auth). |
prepare | Updates package.json version & creates tarball. |
publish | Runs npm publish to the registry. |
(Table adapted from @semantic-release/npm docs.)
4. Create GitHub Releases
To have semantic-release publish a GitHub Release (with release notes), add the @semantic-release/github
plugin. This will automatically create (or update) a release in your GitHub repo whenever a new version is published. For example, extend the config plugins:
// release.config.js
module.exports = {
// ... other config ...
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/npm',
'@semantic-release/github'
]
};
Semantic-release will use the GitHub token from the environment. In GitHub Actions, you can use the built-in ${{ secrets.GITHUB_TOKEN }}
, which is automatically injected if you give the workflow write permissions. The token must have repo
permissions to create releases and tag commits.
(If you protect the branch with required reviews, note that the automatic GITHUB_TOKEN
can’t push back commits. In that case you might use a personal access token with sufficient rights - or skip pushing the version commit - see troubleshooting below.)
5. (Optional) Publish Docker Images
To also publish Docker images on release, use a semantic-release Docker plugin. For example, felixfbecker/semantic-release-docker lets you tag and push Docker images to Docker Hub. A sample JSON config (in .releaserc
) might look like:
{
"release": {
"verifyConditions": {
"path": "semantic-release-docker",
"registryUrl": "docker.io"
},
"publish": {
"path": "semantic-release-docker",
"name": "your-dockerhub-username/imagename"
}
}
}
This tells semantic-release to use the Docker plugin: first it verifies conditions (e.g. logs into the registry using credentials) and then tags & pushes your image to Docker Hub.
You must provide Docker registry credentials in the CI environment. For Docker Hub, set DOCKER_USERNAME
and DOCKER_PASSWORD
(or DOCKER_USERNAME
/DOCKER_PASSWORD
) as secrets. The plugin logs into Docker and then, after the release notes are generated, tags the existing image (built in a prior step) with the new version and latest
, and pushes it. For example, in CI you might do:
- run: docker build -t your-dockerhub-username/imagename .
- run: npm run release # this will invoke semantic-release and the Docker plugin
(Felix’s plugin docs show Travis and Circle CI examples of this flow.) If using GitHub Actions, ensure your runner has Docker (e.g. use setup-docker
or actions/checkout
with Docker, and then login using docker/login-action
with your DOCKER_USERNAME
/DOCKER_PASSWORD
).
6. CI/CD Integration (GitHub Actions)
Semantic-release is designed to run in CI on each push to your release branch. Here’s a minimal GitHub Actions workflow example (from the official guide):
name: Release
on:
push:
branches:
- main # or master
permissions:
contents: write # to create releases
issues: write
pull-requests: write
id-token: write # (for npm provenance, if used)
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # <-- important: get all tags and history
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Install dependencies
run: npm ci
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub auth
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # npm auth
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} # if publishing Docker
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: npx semantic-release
Here we checkout the repo (fetch-depth: 0
to include tags), install deps, then run semantic-release
. The required tokens are passed as environment variables. The permissions
block grants the workflow rights to publish releases and comments. (Adjust or add secrets for Docker if needed.)
You can trigger semantic-release on pull requests or schedule as well, but the common pattern is “on push to main
” so that only merged commits produce a release.
7. Tips & Gotchas
Use Conventional Commits: Semantic-release relies on your commit message format to bump versions correctly. A commit like
feat: add login
will trigger a minor release,fix:
a patch, and includingBREAKING CHANGE:
in the body will trigger a major. If your commits don’t match the expected format, semantic-release will often skip releasing because it sees “no relevant changes.”Protect Your Branch: Important! Once semantic-release is set up, anyone who can push to the release branch (e.g.
main
) can trigger a release. To avoid unauthorized releases, protect the branch (require PR reviews) and restrict who can merge.Checkout Depth: Make sure your CI fetches the full git history. In GitHub Actions, use
fetch-depth: 0
in the checkout step so that semantic-release can find the previous tags and calculate the next version. Otherwise it may think no prior release exists.Push Permissions: Semantic-release typically commits the updated
package.json
/CHANGELOG.md
back to the repo and pushes tags. In GitHub Actions, the defaultGITHUB_TOKEN
won’t push commits if the branch is protected. If you need to commit version bumps back to the main branch, you may have to disable protection or use a Personal Access Token with write permissions (though be careful with security). Alternatively, you can use the@semantic-release/git
plugin to push artifacts explicitly, but note the token limitations.Dry Run First: Test your setup with
npx semantic-release --dry-run
. This will simulate the release process (printing the calculated next version and changelog) without actually publishing or tagging. It’s a good way to debug configuration.Docker Builds: If publishing Docker images, build the Docker image before running semantic-release. In Actions, you could use docker/build-push-action to build and push in one step. Or use the
@semantic-release/exec
plugin to run adocker build
in theprepare
phase as shown in the Docker plugin docs.Monorepos: If you have a monorepo or multiple packages, semantic-release can still work but requires extra config (per-package configs, custom scripts, or tools like semantic-release-multiple). For simplicity, the above assumes a single package repo.
By setting up semantic-release with the right plugins and CI configuration, your project can instantly publish new versions wherever you need (npm registry, GitHub, Docker Hub) whenever commits land in main. This ensures consistent versioning and relieves developers from manual release chores.
Sources
GitHub - semantic-release/semantic-release: :package::rocket: Fully automated version management and package publishing
Configuration | semantic-release
@semantic-release/npm - npm
GitHub Actions | semantic-release
GitHub - esatterwhite/semantic-release-docker: Semantic release plugin for building and pushing docker images