Auto-generating a changelog from git history

At Mozilla we share a lot of open source libraries. When you’re using someone else’s library, you might find that an upgrade breaks something in your application — but why? Glancing at the library’s changelog can help. However, manually maintaining a changelog when building features for a library can be a challenge.

We’ve been experimenting with auto-generating a changelog from commit history itself and so far it makes changelogs easy and painless. Here’s how to set it up. These tools require NodeJS so it’s best suited for JavaScript libraries.

First, you need to write commit messages in a way that allows you to extract metadata for a changelog. We use the Angular conventions which specify simple prefixes like feat: for new features and fix: for bug fixes. Here’s an example of a commit message that adds a new feature:

feat: Added a `--timeout` option to the `run` command

Here’s an example of a bug fix:

fix: Fixed `TypeError: runner is undefined` in the `run` command

The nice thing about this convention is that tools such as Greenkeeper, which sends pull requests for dependency updates, already support it.

The first problem with this is a social one; all your contributors need to follow the convention. We chose to solve this with automation by making the tests fail if they don’t follow the conventions 🙂 It’s also documented in our CONTRIBUTING.md file. We use the conventional-changelog-lint command as part of our continuous integration to trigger a test failure:

conventional-changelog-lint --from master

There was one gotcha in that TravisCI only does a shallow clone which doesn’t create a master branch. This will probably be fixed in the linter soon but until then we had to add this to our .travis.yml:

Alright, everybody is writing semantic commits now! We can generate a changelog before each release using the conventional-changelog tool. Since we adopted the Angular conventions, we run it like this before tagging to get the unreleased changes:

conventional-changelog -p angular -u

This scrapes our commit log, ignores merges, ignores chores (such as dependency updates), ignores documentation updates, and makes a Markdown list of features and fixes linked to their git commit. Example:

### Bug fixes
* Fixed `TypeError: runner is undefined` in the `run` command ([abc1abcd](https://github.com/.../))

### Features
* Added a `--timeout` option to the `run` command ([abc1abcd](https://github.com/.../))

As you can see, we also make sure to write commit messages in past tense so that it reads more naturally as a historic changelog. You can always edit the auto-generated changelog to make it more readable though.

The conventional-changelog tool can update a README.md file but, for us, we just paste the Markdown into our github releases so that it shows up next to each release tag.

That’s it! There are a lot of options in the tools to customize linting commits or changelog generation.