Commitlint and Commitizen are the tools that regulate Git commit information.

Commitlint validates the commit information, commItizen fills in the commit information. In the Git commit workflow, commitLint is applied to the commit-msg phase and commitizen to the pre-commit phase. They do not interfere with each other and complement each other.

It seems that the world is in peace, however, both follow their own rules, and there is no unified interface for each to do their own thing. Collaborating in the same process, configured with two sets of rules, is always a bit of a nuisance.

Why not?

Why can’t you use the same rule configuration? Because the mechanics of the two tools are so different.

commitlint

Commitlint: commitlint: commitlint: commitlint: commitlint: commitlint: commitlint: commitlint: commitlint

We know that one of the most widely agreed base specifications is Conventionalcommits.

<type>[scope]: <subject>

[body]

[footer]
Copy the code

It is made up of type, scope, Subject, body, and footer parts. Commitlint is set up on top of the underlying specification. That is, it believes that a submitted message string can be parses into these parts, and then checks that the parts are compliant.

Once you know how commitLint works, it’s easy to understand how it works. In simple terms, it runs as follows: parse the commit information → determine the rule set → perform the rule check

Parsing the submission information

First it parses the commit information string to a structured object through the parserPreset parser, for example:

For example, the following information is submitted

fix(package1): update title

The old title is overdated

Issues: https://github.com/conventional-changelog/commitlint/issues/2507
Copy the code

Is resolved as a

{
  "header": "fix(package1): update title"."type": "fix"."scope": "package1"."subject": "update title"."body": "The old title is overdated"."footer": "Issues: https://github.com/conventional-changelog/commitlint/issues/2507"."references": [
    "https://github.com/conventional-changelog/commitlint/issues/2507"]}Copy the code

Determine rule set

Next, it generates the rule set from the rule configuration file. Commitlint’s configuration file is commitlint.config.js, which contains the extends, rules, plugins fields, etc. If you’ve used ESLint before, this may sound familiar to those of you who have used ESLint, but the extends field in a configuration similar to ESLint inherits the existing configuration, and rules defines the rules.

In a nutshell, this step resolves the configuration file into a set of rules that can be used later. Let’s take a short look at what the rule set looks like:

{
    'subject-full-stop': [2, 'never', '.'],
    'subject-case': [
        2,
        'never',
        ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
    ],
    'subject-empty': [2, 'never'],
}
Copy the code

The key is the rule name and the value is the rule parameter. A rule actually corresponds to a logical function. Commitlint’s built-in rule logic is maintained by a list of key-value pairs. Let’s take a look at the subject-full-stop logical function.

// subject-full-stop.ts
import message from '@commitlint/message';
import {SyncRule} from '@commitlint/types';

export const subjectFullStop: SyncRule<string> = (
	parsed,
	when = 'always',
	value = '. '
) = > {
	const input = parsed.subject;

	if(! input) {return [true];
	}

	const negated = when === 'never';
	const hasStop = input[input.length - 1] === value;

	return [
		negated ? !hasStop : hasStop,
		message(['subject', negated ? 'may not' : 'must'.'end with full stop']]), };Copy the code

The first argument parsed to the subject-full-stop function is the structure object generated in the first step; The second parameter, when, is the second element in the rule value and represents either the conditional prefix is or is not; The third parameter, value, is the third element in the rule value and represents the specific value.

The subject-full-stop function returns the verification result and an error message.

Now let’s look at what the rules mean, for example

  • 'subject-full-stop': [2, 'never', '.']: indicates that the title cannot be.The end of the
  • 'subject-full-stop: [2, 'always', '!']': indicates that the title must end with!The end of the

Looking back at the logic of the subject-full-stop function above, it does.

Performing a rule check

At this stage, commitlint iterates over the ruleset and executes the corresponding logical functions. Finally, the validation results are formatted and the report is output.

commitizen

Commitlint communicates directly with configuration files. Unlike commitizen, commItizen itself has no relation to the commit information rules. Commitizen provides only a framework for interacting with Git. It passes Inquirer objects to adapters that describe the command line filling process and return information that the user has filled out. So what really matters, and what is relevant to the rules, is the adapter.

inquirerIs a toolset that encapsulates common command-line interfaces, such as Input, confirm, list, Checkbox, editor, etc.

Most Commitizen adapters describe the command line interaction process according to a specific specification, such as the CZ-Conventional Changelog based on the Conventional Changelog specification. With this mechanism, if developers wanted to make their own rules, they had to learn to use the Inquirer to create a new adapter from scratch, which was not easy to use. Is there any support for custom adapters? Yes, cz-customizable is the existence of the special.

Cz-customizable externalizes a configuration file.cz-config.js, designs some configurable fields, and cZ-Customizable reads the configuration and converts it into an Inquirer based command line interaction process.

The following is an example of the cz-Customizable configuration:

module.exports = {
  // Type Indicates an optional list
  types: [{value: 'feat'.name: 'feat: A new feature' },
    { value: 'fix'.name: 'fix: A bug fix'},].// Scope Optional list
  scopes: [{ name: 'accounts' }, { name: 'admin' }, { name: 'exampleScope' }, { name: 'changeMe'}].// Overwrite the interactive prompt
  messages: {
    type: "Please select your submission type".scope: 'Please select SCOPE'.subject: 'Brief description of this modification :\n'.body: 'provides information about the modifications more specific (optional), use "|" line: \ n'.confirmCommit: 'Are you sure to submit information? ',},// skip any questions you want
  skipQuestions: ['body'].// limit subject length
  subjectLimit: 100};Copy the code

How to achieve sharing?

Now that we understand the mechanisms of CommitLint and Commitizen, let’s consider the core issue: how to get both of them to share the same rule configuration.

There are two ideas:

  1. From the commitLint configuration, read the commitLint configuration and generate the corresponding command line commit process, that is, create a CommItizen adapter, @commitLint /cz-commitlint implemented
  2. From the z-Customizable configuration, translate the Z-Customizable configuration to the commitLint rule, that is, create a commitlint configuration, commitlint-config-cz has been implemented

cz-commitlint

The @commitlint/cz-commitlint configuration process is simple

Install commitizen and commitLint and cz-commitLint
npm install --save-dev @commitlint/cz-commitlint commitizen @commitlint/cli
# or yarn
yarn add -D @commitlint/cz-commitlint commitizen @commitlint/cli
Copy the code

Configure the CommItizen adapter in package.json

{
  "scripts": {
    "commit": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "@commitlint/cz-commitlint"}}}Copy the code

Install and configure the required specifications, such as the Conventional – Changelog specification

npm install --save-dev @commitlint/config-conventional
# or yarn
yarn add -D @commitlint/config-conventional

# configuration commitlint. Config. Js
echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
Copy the code

commitlint-config-cz

The configuration process for commitlint-config-cz is also not complex

Install Commitizen and commitlint and cz-customizable and commitlint-config-cz
npm install --save-dev commitlint-config-cz commitizen cz-customizable @commitlint/cli
# or yarn
yarn add -D commitlint-config-cz commitizen cz-customizable @commitlint/cli
Copy the code

Configure the CommItizen adapter in package.json

{
  "scripts": {
    "commit": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"}}}Copy the code

Set it in commitlint.config.js

module.exports = {
    extends: [
        'cz']};Copy the code

Configure.cz-config.js, optionally based on the official template.

How to choose

Both have applicable scenarios

@commitLint /cz-commitlint is suitable for using an existing specification or for custom extensions based on an existing specification.

Commitlint-config-cz is suitable for full customization scenarios that do not depend on existing specifications.

Z-commitlint can also implement full customization, but it requires understanding commitLint’s configuration and rules, which is a bit more of a hurdle than the simple and intuitive configuration of.cz-config.js, but this may not be an issue for front-end developers who are comfortable with ESLint.