Setup Husky and Commitlint to our repo

Sometimes, we put anything in our code commits, but this doesn't help when checking what changes were made if it was a fix or part of a new feature. Keeping order in our repositories is important. Tools like Husky and Commitlint help us add structure to our work. I'll show you how to use these tools to make this task more organized.

Husky

Husky is a tool for developers that works with Git. It's like a guard: when you "commit" (save changes to your code), Husky checks if everything follows the rules you set (like code style or tests). If something isn't right, Husky stops it until you fix it. This helps keep your code clean and without errors before saving it permanently.

Install Husky with yarn

yarn add --dev husky

Init the configuration

npx husky init

This creates the file .husky/pre-commit and adds it to our package.json in the scripts section:

"scripts": {
    "prepare": "husky" // <- added this automaticly
}

In our case, prepare doesn't work with yarn, so we'll change it from prepare to postinstall, making it look like this:

 "scripts": {
    "start": "nx serve",
    "build": "nx build",
    "test": "nx test",
    "postinstall": "husky" // <- we use yarn
  },

The magic comes from the .husky/pre-commit file, which you can think of as a bash file, where each line is a command that will run. In our case, before each commit, we want to execute:

  • nx format:write

  • nx affected -t lint,test --parallel=2

With these, we ensure that before making a commit, our affected files are formatted, and then it checks the lint and makes sure all tests are running perfectly.

Now, when we make a commit, something like this happens:

Great, we now have Husky installed and configured. If something goes wrong, the commit won't be executed, giving us the chance to fix the lint or tests locally (instead of finding out through the GitHub Action).

Commit Lint

Commitlint is a software development tool that ensures commit messages (the records of code changes) follow a certain format. It works like an automatic checker, looking at each commit message before it's confirmed. This helps keep the project's change history clear and consistent.

We install commitlint

yarn add -d @commitlint/{config-conventional,cli}

Create the configuration file:

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

Husky + Commitlint

To have Husky detect the commit, we'll add another Git hook (just like we did with pre-commit), in this case, it's commit-msg. We'll create a file in .husky/commit-msg and add the following:

npx --no -- commitlint --edit ${1}

Now, when we try to make a simple commit and it doesn't have the correct format, we see something like this:

So, the commit doesn't go through because it doesn't have the correct format.

But what is the correct format? Well, commitlint follows something called conventional commit format, which expects a commit message with the format <type>[optional scope]: <description>

By default, we have these types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test

For example, if we want to define the list of types and scopes to use, we can modify the configuration file commitlint.config.js with something like this:

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'],
    ],
    'scope-enum': [
      2,
      'always',
      ['api', 'frontend', 'backend', 'ui', 'database'],
    ],
  },
};

What does the added rules :

  • 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']]: This defines the allowed commit types. The custom types here are feat, fix, docs, style, refactor, test, and chore. These represent different kinds of changes you might make in your project (new features, fixes, documentation, etc.).

  • 'scope-enum': [2, 'always', ['api', 'frontend', 'backend', 'ui', 'database']]: Here, in addition to the scopes api, frontend, and backend, we add ui and database as valid scopes.

  • The 2 means that this is a rule that must always be followed (error if not met), and always indicates that this rule is always active.

With this configuration, a commit message must use one of the defined types and one of the defined scopes. For example:

git commit -m "feat(ui): add new button component"

This message indicates that a new feature (feat) related to the user interface (ui) is being added.

If are you using Angular can use this (link) default configuration, or Nx, this link to get all the scopes of your Nx apps to dynamically generate the scopes.

Conclusion

Husky and Commitlint are helpful tools for keeping code quality and consistency in a project. Husky checks code against rules before a commit, while Commitlint makes sure commit messages follow a specific format. This keeps the project's change history clear and consistent. By using these tools in your development process, you can avoid errors, enforce code style, and keep a clean commit history. This improves readability and makes teamwork more efficient.

Resources

5
Subscribe to my newsletter

Read articles from Arcadio Quintero directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arcadio Quintero
Arcadio Quintero

Frontend developer at @dotCMS โ€ข Angular Developer โ€ข System Engineer โ€ข ๐Ÿ‘‹ Passionate software engineer from Panama ๐Ÿ‡ต๐Ÿ‡ฆ located in Ontario, Canada ๐Ÿ‡จ๐Ÿ‡ฆ , with over 16 years of experience in web development. ๐Ÿ’ป I've worked extensively with various programming languages such as PHP, Typescript, and JavaScript. I have deep knowledge of frameworks like Angular, NestJS, and CakePHP, leveraging their power to create robust and scalable applications. ๐ŸŒŸ I'm passionate about continuous learning and staying updated with the latest trends in the ever-evolving field of software development.