A changelog is a detailed record of any changes you’ve made to your project over a period of time. Not only does a changelog serve as a starting point for fixing bugs and errors, but it is also a valuable educational resource when introducing new developers to your project.
In this tutorial, we’ll explore a method for automatically generating and releasing a changelog that uses git hooks and Node.js. We’ll create a conventional commit message using a specific commit format called Conventional Commits and a tool called Commitizen. Then, we’ll use a library called standard-version to automatically generate a changelog and a new release version that follows semantic versioning.
Finally, we’ll make our changelog shareable across the development team so that everyone follows the same conventions in the project. You can find the final code in this GitHub repository if you wish to follow along.
Let’s get started!
Structuring commit messages in Conventional Commits
The Conventional Commits specification improves commit messages by providing rules for creating a specific commit history. Conventional Commits makes generating a changelog easy by creating a release that uses semantic versioning.
According to convention, the commit message should be structured as follows:
Let’s examine the detail of the structure:
<type>
is a type of commit that affects the version number of the release. In semantic versioning, the fix
type affects PATCH and the feat
type affects MINOR. There are other types, however, these don’t affect the version number of the release.
scope
is an optional noun that describes the part of the codebase that is changed or updated by the commit. For example, in feat(pages)
, pages is the scope.
In semantic versioning, !
correlates with MAJOR. When used after the scope, !
indicates that there are breaking changes in the commit.
<description>
is a brief, written explanation of the changes made to the code. For example, if we wrote a description for feat(pages)
, it could look like the following: feat(pages): add contact page in the side menu
.
body
is an optional field that you can use to describe the commit in more detail. body
must begin one line after the description. footer
is also an optional field. For example, one footer is BREAKING CHANGE
, which would correlate with MAJOR in semantic versioning.
Commit message examples
Let’s look at some examples of different commit messages:
Commit message with just type
and description
:
Commit message with type
, scope
, and description
:
Commit message with BREAKING CHANGE
:
Creating our project
Let’s start our project by adding the necessary tools to automate our changelog and release. First, create a command
prompt, where we’ll add the following code blocks.
Let’s create an npm-based project and make it a git repository. If you want to automate an existing repository, you can skip this step:
The code block above will create a git repository and an npm package with v1.0.0.
Add standard-version
to our project
Now, let’s begin creating releases for our project! You’ll need to install the standard-version
npm package into your project as follows:
You’ll also need to add it into npm scripts:
Creating a release
Create a dummy file called new-feature
and commit it as follows:
Add the following git commit message:
Finally, let’s create a release in our project by running our newly added script:
Running the command above will show the following message on the screen:
The message above does the following:
- Increases the SemVer version number from
1.0.0
to1.1.0
We added one feature, therefore, MINOR was updated from0
to1
- Creates a
CHANGELOG.md
file, adding the required content to it - Commits the changes above, creating a
v1.1.0
tag - Prints out a message to push tags and publish our package to npm, if needed
CHANGELOG.md
Now, if you open CHANGELOG.md
, you’ll see the following code block, which includes the changes made above:
You’ll also see the commit message standard-release
created, which used the git log -1
command to make a release:
The type of commit message is chore
, the scope is release
, and the description
is 1.1.0
.
Now, you have everything you need to automate your changelog and release! However, manually writing the commit is tedious and error-prone. Let’s bring in some tools to smooth the process!
Adding Commitizen
Instead of writing conventional commits yourself, you can use Commitizen to auto-generate them. Commitizen asks you questions in the command
prompt and generates the commits based on your answers.
Install the Commitizen package as follows:
Now, initialize Commitizen to use the conventional changelog adapter:
An adapter is a configuration that tells Commitizen to display different kinds of commits in a prompt. Currently, there are a variety of adapters available, but you can create your own adapter if you wish.
Now, to use Commitizen, we’ll add an npm script:
At this point, you should create a .gitignore
file and ignore the node_modules
directory.
Add package.json
and package-lock.json
to the git staging area using git add
. We’ll make a commit by running the code block below:
The code block above will also prompt you to answer the directives that follow.
type
shows the list of types
from which you can select. The list below came from the adapter that we installed earlier:
scope
, in the code block below, refers to the scope of the conventional commit:
For short description
, write a brief explanation of the conventional commit:
In longer description
, describe the body
of the conventional commit:
The two questions in the code block below generate a commit with breaking changes:
In the section for issues related to commit
, you can reference issues from GitHub, JIRA, or other similar tools:
Once you’ve answered these prompts according to your needs, you’ll have a commit like the one shown below:
Adding commitlint to enforce rules
To ensure that all the developers on our project follow the same conventions, we’ll use git hooks with Husky and commitlint.
Installing required tools
First, let’s install commitlint and Husky by running the code block below:
Configure commitlint
To configure commitlint, we’ll need to create a config file named commitlint.config.js
and add the following code:
To lint messages before they are committed, we need to use the commit-msg
hook from Husky by running the following commands:
You can add husky install
as an npm prepare script, however, this step is optional. husky install
will ensure that every developer using this repo will install Husky Hooks before using the project:
We’ll still use git commit to make our commits follow the convention described earlier. If there is a mistake in the git message, commitlint will raise the following errors:
Final workflow for managing releases
To manage your releases, you can follow the workflow listed below:
- Create your features and commit them. If commit messages aren’t following convention, commitlint will raise errors
- Execute the
npm run commit
in the command line to make a commit with Commitizen - Run
npm run release
to create a changelog and a semantic versioning-based release
To create a release using CI/CD, look at the semantic release.
Summary
In this post, you learned how to create an automatic changelog and a semantic versioning-based release using git hooks and Node.js. We created our commit message using the Conventional Commits specification, then released it using commitizen and standard-release
. Next, we used commitlint and Husky to automatically write our commit.