How to automate your releases, versioning & release notes like a boss

Whether you have an open source project or work in a private company on a project that other people use in their projects, you probably need to communicate the changes you make to your project, so that others can understand what changed and know if there’re any breaking changes. Some reasons you might want to automate your releases are to:

  1. Follow semantic versioning in order to reflect project changes to consumers and differentiate between features releases, bug fixes and breaking changes.
  2. Generate release notes/changelog so that other developers using your library can understand what exactly are the changes included in every release and their implications.
  3. Communicate changes to clients and/or project managers.
  4. Make releases part of your continuous integration system.
  5. The process of manually generating releases, bumping versions and creating release notes can be painful, error prone and emotional.
  6. Automatically create a git tag and publish the new version of your library on npm.

What if every pull request or commit you made to your release branch would automatically generate you a changelog, figure out the required version bump if any, bump the version for you as well as release your library automatically?

Meet semantic-release. Before we set things up there are two requirements for using semantic-release: “Semantic Versioning” and “Conventional Commits”.

Semantic Versioning

Semantic versioning provides a way to have meaningful version numbers that represent that development of your project.

Given a version number MAJOR.MINOR.PATCH, increment the:

MAJOR version when you make incompatible API changes,

MINOR version when you add functionality in a backwards-compatible manner, and

PATCH version when you make backwards-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

Conventional Commits

Conventional commits are a specification for adding human and machine readable meaning to commit messages

If you’re not familiar with conventional commits take a look at conventional commits or Angular commit style. Basically your commit messages should start with one of the following.

Semantic-Release Configuration

Now to the actual setup:

1. First you need to install semantic-release and the plugins you intend to use based on your configuration, you can see a list of all the available plugins here.

npm i -D semantic-release @semantic-release/

2. Next, follow the documentation here to configure semantic release based on your own needs. My personal configuration looks as follows:

// package.json
"release": "plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
// if you dont want to publish on npm registry, you can set npmPublish to false
["@semantic-release/npm",],
"@semantic-release/changelog",
"@semantic-release/git"
],
"branch": "master"
>

3. You should now be able to automatically create a release by running npx semantic-release from your Continuous Integration system or even manually with the --no-ci flag.

Continuous Integration Setup

Manual releases With Github

All you need is to set a GH_TOKEN environment variable and you’re good to go.

With Bitbucket Pipelines

You can use one of the following CI recipes, in my case I’m using a private bitbucket repository and pipelines as a CI along with a private npm registry where I want to publish my library to, so we need to do some extra work.

  1. You can follow the steps provided here by npm to configure pipelines with your private registry and npm token.
  2. First we need to give access to our CI to publish our library on npm and set the npm registry we’re using in case we’re publishing to a private registry. We can do this by setting the NPM_REGISTRY_URL & NPM_TOKEN environment variables in the repository’s pipelines’ settings.
  3. Add an SSH Key to your pipelines settings from the repository’s pipelines’ settings on bitbucket so that it can access the private repository and can push release tags.
  4. Create a bitbucket-pipelines.yml in the root directory of the repository that will configure the build pipelines and trigger semantic-release via npx.
# This is a sample build configuration for JavaScript.
# Check our guides at https://confluence.atlassian.com/x/14UWN for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: node:latest
pipelines:
default:
- step:
caches:
- node
script: # Modify the commands below to build your repository.
# Generates a .npmrc file configured for installing private modules:
#
# NPM_REGISTRY_URL: the full URL of your private registry
# defaults to registry.npmjs.org.
# NPM_TOKEN: secret token for installing private modules. This
# this token can be found in your .npmrc, after logging in.
- printf "//`node -p \"require('url').parse(process.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org').host\"`/:_authToken=$\nregistry=$\n" >> ~/.npmrc
- npm install
- npm test
- npx semantic-release

If you set everything up correctly, now every commit that will land in the master branch will trigger a release and execute the configured plugins. When merging feature pull requests you can use the Squash merge strategy which will squash an entire pull request of multiple commits into a single commit where you can even change the commit message to a conventional commit style one.

Make a conventional commit style commit message, merge it to the master branch and watch the magic happen.

A new pipeline was triggered and marked as green, semantic-release pushed a new commit generating the CHANGELOG.md file based on the previous commits and a new git tag was created with the semantically correct version and the package was published to my private npm registry. Pretty awesome, isn’t it?