Contents
- Introduction
- Conventional commits
- Enforcing conventional commits
- Automating with semantic-release
- Conclusion
Introduction
One of the challenges when trying to automate npm package releases is semantic versioning. It is quite common for developers to "manually" decide what the next version bump should be, which makes the decision biased and sentimental.
The obvious way to remove that subjective bias is to automate the version bump and, to do this, we will need machine readable information in the codebase to help determine the next version. Enter conventional commits!
Conventional commits
As described in the conventional commits website, it is:
A specification for adding human and machine readable meaning to commit messages.
It provides a set of rules for creating a commit history that can be easily used by automated tools such as a CI/CD pipeline.
Conventional commits should use the following structure:
<type>(optional scope): <description>
[optional body]
[optional footer(s)]
Here's a more concrete example:
feat(server): upgrade json parsing library
BREAKING CHANGE: replacing the library introduces a breaking change
For now, all you need to know to get started are these conventions:
-
fix
commit type - indicates a bugfix and corresponds to a PATCH update -
feat
commit type - indicates an introduced feature and corresponds to a MINOR update -
BREAKING CHANGE:
footer comment - indicates a breaking change and corresponds to a MAJOR update
You can learn more about the full specification for conventional commits on the Conventional Commits website.
Enforcing conventional commits
There are many tools available to help with enforcing conventional commits in your codebase. In this example, we'll be using husky and commitlint.
Install husky and commitlint by running the following command.
npm install --save-dev husky @commitlint/config-conventional @commitlint/cli
Then add the following configuration to your project's package.json
file.
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
Now if you try adding a commit message that doesn't conform to the conventional commits spec, you will get an error message, and the commit will fail.
git commit -m 'some random commit'
husky > commit-msg (node v12.16.1)
⧗ input: some random commit
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
Automating with semantic-release
Now that your codebase is using conventional commits, you have a commit history that is machine readable, which means you can start automating your version updates and release.
Again, there are numerous tools out there that help facilitate this, but we've chosen semantic-release and GitHub Actions as an example.
semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes and publishing the package.
Feel free to read more about these tools in the official documentation as the following examples are very basic.
To get started, install semantic-release first.
install --save-dev semantic-release
Then, add the following to the scripts section of your package.json
file.
{
"scripts": {
"semantic-release": "semantic-release"
}
}
You can test if versioning works by running semantic-release in dry-run mode.
npm run semantic-release -- --dry-run
Next, let's set up our Github Action workflow. Add a new directory in your project's codebase called .github/workflows
. In this directory, add a new YAML file, build.yml
with the following contents.
name: Build
on: [push]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install npm dependencies
run: npm install
- name: Run tests
run: npm test
- name: Package
if: github.ref == 'refs/heads/master'
run: npm run semantic-release
This will create a GitHub Action workflow called Build
which will be triggered every time you push a commit to GitHub. The workflow will go through the following steps.
- Spin up a docker container with Ubuntu.
- Install Node.js
- Check out the git repo.
- Install the npm dependencies for the project.
- Run tests.
- Execute semantic-release if the current branch is
master
. This step will essentially do the following tasks.- Automatically determine the next version using information gathered from commit messages
- Bump the version up in package.json
- Publish the package to the registry
- Create a new tag/release on GitHub
There you have it! Your npm release is now fully automated!
Conclusion
As we mentioned before, one of the biggest challenges with automating semantic versioning is finding a way to have machine readable information that automation tools can use to determine version updates.
We established that Conventional Commits would be able to address this by providing a structure that would allow both humans and machines to objectively determine the "extent" of the changes included in the new version.
You can use whatever tools best suit your project but here are some that have proven to be quite useful.
- husky for commit hooks
- commitlint for enforcing conventional commit messages
- semantic-release for full automation of versioning and releasing of npm packages
Hope this post was helpful! I'm also keen to hear your ideas on the topic so feel free to comment.
Top comments (0)