Automatically generate and release a changelog using Node.js
Tags: node, github, webdev
Published: 2021-08-02 18:05:13

**Written by [Indermohan Singh] (https://blog.logrocket.com/author/indermohansingh/)** ✏️

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](http://conventionalcommits.org/) and a tool called [Commitizen](https://github.com/commitizen/cz-cli). Then, we’ll use a library called [`standard-version`](https://www.npmjs.com/package/standard-version) to automatically generate a changelog and a new release version that follows [semantic versioning](https://blog.logrocket.com/never-guess-about-project-history-again-31f65091f668/).

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](https://github.com/imsingh/changelog) 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:

```
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
```

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`:

```
feat: add the charging option for cars
```

#### Commit message with `type`, `scope`, and `description`:

```
fix(homepage): change title width of title
```

#### Commit message with `BREAKING CHANGE`:

```
refactor(api): remove the get api from reservations

BREAKING CHANGE: refactor to use the trip api instead of reservations api
```

## 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:

```
# create project directory
mkdir changelog

# cd into project
cd changelog

# initialize npm project
npm init -y

# initialize git
git init
```

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:

```bash
npm install --save-dev standard-version
```

You’ll also need to add it into npm scripts:

```json
...
"scripts": {
  "release": "standard-version"
}
...
```

### Creating a release

Create a dummy file called `new-feature` and commit it as follows:

```
touch new-feature
git add new-feature
git commit
```

Add the following Git commit message:

```
feat(new-feature): add a new-feature to our project
```

Finally, let’s create a release in our project by running our newly added script:

```bash
npm run release
```

Running the command above will show the following message on the screen:

```
> changelog@1.0.0 release /home/imsingh/Develop/inder/changelog
> standard-version

✔ bumping version in package.json from 1.0.0 to 1.1.0
✔ bumping version in package-lock.json from 1.0.0 to 1.1.0
✔ created CHANGELOG.md
✔ outputting changes to CHANGELOG.md
✔ committing package-lock.json and package.json and CHANGELOG.md
✔ tagging release v1.1.0
ℹ Run `git push --follow-tags origin master && npm publish` to publish
```

The message above does the following:

*   Increases the SemVer version number from `1.0.0` to `1.1.0` We added one feature, therefore, MINOR was updated from `0` to `1`
*   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:

```
# Changelog

All notable changes to this project will be documented in this file. See \[standard-version\](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## 1.1.0 (2021-07-12)

### Features

* **new-feature:** add a new-feature to our project 11c0322
```

You’ll also see the commit message `standard-release` created, which used the `git log -1` command to make a release:

```
commit  #COMMIT_HASH (HEAD -> master, tag: v1.1.0)
Author: #AUTHOR_NAME <#AUTHOR_EMAIL>
Date:   #COMMIT_DATE

    chore(release): 1.1.0
```

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:

```bash
npm install --save-dev commitizen
```

Now, initialize Commitizen to use the conventional changelog adapter:

```bash
npx commitizen init cz-conventional-changelog --save-dev --save-exact
```

An adapter is a configuration that tells Commitizen to display different kinds of commits in a prompt. Currently, there are a variety of [adapters](https://github.com/commitizen/cz-cli#adapters) available, but you can create your own adapter if you wish.

Now, to use Commitizen, we’ll add an npm script:

```json
...
"scripts": {
    "commit": "cz"
}
...
```

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:

```bash
npm run commit
```

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:

```
? Select the type of change that you're committing: 
  feat:     A new feature 
  fix:      A bug fix 
  docs:     Documentation only changes 
❯ style:    Changes that do not affect the meaning of the code (white-space, formatting, missing semi-col
ons, etc) 
  refactor: A code change that neither fixes a bug nor adds a feature 
  perf:     A code change that improves performance 
(Move up and down to reveal more choices)
```

`scope`, in the code block below, refers to the scope of the conventional commit:

```
? What is the scope of this change (e.g. component or file name): (press enter to skip) 
```

For `short description`, write a brief explanation of the conventional commit:

```
? Write a short, imperative tense description of the change (max 82 chars):
```

In `longer description`, describe the `body` of the conventional commit:

```
? Provide a longer description of the change: (press enter to skip)
```

The two questions in the code block below generate a commit with breaking changes:

```
? Are there any breaking changes?
? Describe the breaking changes:
```

In the section for `issues related to commit`, you can reference issues from GitHub, JIRA, or other similar tools:

```
? Does this change affect any open issues?
? Add issue references (e.g. "fix #123", "re #123".):
```

Once you’ve answered these prompts according to your needs, you’ll have a commit like the one shown below:

```
Author: #AUTHOR_NAME <#AUTHOR_EMAIL>
Date:   Mon Jul 12 21:10:17 2021 +0200

    feat(some-scope): a short description

    a long description

    BREAKING CHANGE: it breaks

    123
```

## 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](https://typicode.github.io/husky/#/) and commitlint.

### Installing required tools

First, let's install commitlint and Husky by running the code block below:

```bash
# Install commitlint cli and conventional config
npm install --save-dev @commitlint/config-conventional @commitlint/cli

# Install Husky
npm install husky --save-dev
```

### Configure commitlint

To configure commitlint, we’ll need to create a config file named `commitlint.config.js` and add the following code:

```javascript
module.exports = {extends: ['@commitlint/config-conventional']}
```

To lint messages before they are committed, we need to use the `commit-msg` hook from Husky by running the following commands:

```bash
# Activate hooks
npx husky install

# Add hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
```

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:

```json
... 
"scripts": {
...
  "prepare": "husky install"
}
```

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:

```
git commit -m "This is a commit"  
⧗   input: This is a 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\](https://github.com/conventional-changelog/commitlint/#what-is-commitlint)  

husky - commit-msg hook exited with code 1 (error)
```

### Final workflow for managing releases

To manage your releases, you can follow the workflow listed below:

1.  Create your features and commit them. If commit messages aren’t following convention, commitlint will raise errors
2.  Execute the `npm run commit` in the command line to make a commit with Commitizen
3.  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](https://github.com/semantic-release/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.

---

## 200’s only ✔️ Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket] (https://logrocket.com/signup/).

[![LogRocket Network Request Monitoring] (https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://logrocket.com/signup/)

[LogRocket] (https://logrocket.com/signup/) is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. [Start monitoring for free] (https://logrocket.com/signup/).