ngx-semantic-version: enhance your git and release workflow

Stichwörter

Original veröffentlicht auf angular.schule.com.

In this article I will introduce the new tool ngx-semantic-version. This new Angular Schematic allows you to set up all necessary tooling for consistent git commit messages and publishing new versions. It will help you to keep your CHANGELOG.md file up to date and to release new tagged versions. All this is done by leveraging great existing tools like commitizen, commitlint and standard-version.


TL;DR

ngx-semantic-version is an Angular Schematic that will add and configure commitlint, commitizen, husky and standard-version to enforce commit messages in the conventional commit format and to automate your release and Changelog generation by respecting semver. All you have to do for the setup is to execute this command in your Angular CLI project:

ng add ngx-semantic-version

Introduction

Surviving in the stressful day-to-day life of a developer is not easy. One feature follows the other, bug fixes and breaking changes come in on a regular basis. With all the hustle and bustle, there's literally no time to write proper commit messages.

If we don't take this job serious, at the end of the day our git history will look like this:

* 65f597a (HEAD -> master) adjust readme
* f874d16 forgot to bump up version
* 3fa9f1e release
* d09e4ee now it's fixed!
* 70c7a9b this should really fix the build
* 5f91dab let the build work (hopefully)
* 44c45b7 adds some file
* 7ac82d3 lots of stuff
* 1e34db6 initial commit

When you see such a history you know almost nothing: neither what features have been integrated nor if there was a bugfix or a breaking change. There is almost no meaningful context.

Wouldn't it be nice to have a cleaner git history that will follow a de facto standard which is commonly used?

But more than this: having a clean and well-formatted git history can help us releasing new software versions respecting semantic versioning and generating a changelog that includes all the changes we made and references to the commits.

No more struggle with forgotten version increasements in your package.json. No more manual changes in the CHANGELOG.md and missing references to necessary git commits. Wouldn't it be nice to automate the release process and generate the changelog and the package version by just checking and building it from a clean git history? And wouldn't it be nice to add all this stuff with one very simple single line command to your Angular project?

ngx-semantic-version will give you all that.

What does it do?

ngx-semantic-version will add and configure the following packages for you. We will take a look at each tool in this article.

  • commitlint: check commit messages to follow the conventional commit pattern
  • husky: hook into git events and run code at specific points (e.g. at commit or push)
  • commitizen: helper for writing conventional commit messages
  • standard-version: generate conventional changelogs from the git history

commitlint: Enforcing conventional commit messages

Commitlint will give you the ability to check your commit messages for a common pattern. A very prominent project following this pattern is the Angular repository itself. The conventional-commit pattern requires us to follow this simple syntax:

<type>[optional scope]: <description>

[optional body]

[optional footer]

Let's see what is the meaning of these parameters:

  • type can be one of the following codes:
    • build
    • ci
    • chore
    • docs
    • feat
    • fix
    • perf
    • refactor
    • revert
    • style
    • test
  • scope is optional and can be used to reference a specific part of your application, e.g. fix(dashboard): add fallback for older browsers
  • The description is mandatory and describes the commit in a very short form (also called subject)
  • If necessary, a body and a footer with further information can be added which may contain:
    • The keyword BREAKING CHANGES followed by a description of the breaking changes
    • A reference to a GitHub issue (or any other references, such as JIRA ticket number)

An example message could look like that:

refactor(footer): move footer widget into separate module

BREAKING CHANGES
The footer widget needs to be imported from `widgets/FootWidgetModule` instead of `common` now.

closes #45

Following this pattern allows us to extract valuable information from the git history later. We can generate a well-formatted changelog file without any manual effort. It can easily be determined what version part will be increased and much more.

You may think now: "Wow, that style looks complicated and hard to remember." But don't worry: you will get used to it soon! In a second you will see how creating these messages can be simplified using commitizen.

If you want to try you commitlint separately, you can even try it out using npx:

commitlint cli

ngx-semantic-version will add the configuration file commitlint.config.js which can be adjusted later by your personal needs.

husky: Hook into the git lifecycle

Husky allows us to hook into the git lifecycle using Node.js. We can use husky in combination with commitlint to check a commit message right before actually commiting it. This is what ngx-semantic-version configures in our application. It will add this part to your package.json:

...
"husky": {
  "hooks": {
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
},

Husky uses the environment variable HUSKY_GIT_PARAMS containing the current git message you entered and it will pass this through commitlint so it can be evaluated.

Whenever you commit, commitlint will now automatically check your message.

commitizen: Easily write conventional commit messages

Defining a well-formed message text can be quite hard when you are not used to the conventional-changelog style. The tool commitizen is there to help beginners and to prevent your own negligence. It introduces a lots of restrictions for our commit messages so that it's easier for developers to follow the pattern. Commitizen will help you to always define a commit message in the appropriate format using an interactive CLI:

commitizen cli

When adding ngx-semantic-version it will configure commitizen to use the conventional changelog style as well:

// package.json
...
"config": {
  "commitizen": {
    "path": "./node_modules/cz-conventional-changelog"
  }
}

If you are using Visual Studio Code, you can also use the extension Visual Studio Code Commitizen Support which will let you type the commit message directly in the editor:

commitizen vscode plugin

standard-version: Generate changelogs from the git history

Standard-version is the cherry on the cake and takes advantage of a well-formed git history. It will extract the commit message information like fix, feature and BREAKING CHANGES and use this information to automatically create a CHANGELOG.md file. The tool will also determine the next version number for the project, according to the rules of semantic versioning.

ngx-semantic-version will configure a new script in your package.json that can be used for releasing a new version:

...
"scripts": {
  "release": "standard-version",
},

Whenever you want to release a version, you should use standard-version to keep your versioning clean and the CHANGELOG.md up-to-date. Furthermore, it references both commits and closed issues in your CHANGELOG.md, so that it's easier to understand what is part of in the release. The tool will also tag the version in the git repo so that all versions will be available as releases via GitHub, Gitlab or whatever you are using.

How to use ngx-semantic-version

Are you excited, too? Then let's get started! Configuring all mentioned tools manually can be quite tedious. Here is where ngx-semantic-version enters the game: It is an Angular schematic that will add and configure all the tools for you.

All we need it to run the following command:

ng add ngx-semantic-version

After installation, your package.json file will be updated. You will also find a new file commitlint.config.js which includes the basic rule set for conventional commits. You can adjust the configuration to satisfy your needs even more.

Try it out and make some changes to your project! Commitlint will now check the commit message and tell you if it is valid or not. It prevents you from commiting with a "bad" message. To make things easier, commitizen will support you by building the message in the right format and it even explicitly asks you for issue references and breaking changes.

If you typically use npm version to cut a new release, now you do this instead:

npm run release

You should also consider using one of the following commands:

npm run release -- --first-release  # create the initial release and create the `CHANGELOG.md`
npm run release -- --prerelease     # create a pre-release instead of a regular one

standard-version will now do the following:

  1. "Bump" the version in package.json
  2. Update the CHANGELOG.md file
  3. Commit the package.json and CHANGELOG.md files
  4. Tag a new release in the git history

Check out the official documentation of standard-version for further information.

Conclusion

I hope that ngx-semantic-version will make your daily work easier! If you have a problem, please feel free to open an issue. And if you have any improvements, I'm particularly happy about a pull request.

Happy coding, committing and releasing!


Thank you

Special thanks go to Ferdinand Malcher and Johannes Hoppe for revising this article and discussing things.