Closed7

【必要最小限】git commit をする際のメッセージを全員で統一させる【husky/commitlint/git-cz】

harrythecodeharrythecode

何をしたいか

git commit -m "<Anything>" で自由にメッセージを記述できるのはみなさんご存知ですが、この中身を複数人で統一させる為にはどうすれば良いかをまとめていきます。

CI/CDでお馴染みの「semantic release」にも対応しているので一石二鳥です。

ではみていきます。

harrythecodeharrythecode

必要なもの

  • Husky:
    • Gitコマンド実行時より先に何か特定のコマンド(Commitlint)を実行させる為に必要
  • Commitlint:
    • Commitメッセージをある一定のルールに基づいて書かれているかをチェックさせる。ルールに一致しない場合はCommitさせないようにできるので必要
  • git-cz:
    • Commitlintだけだと「ルールと一致しません」程度のエラーメッセージしか吐き出さず、全てのルールを頭の中で把握するのは非常に面倒です。そこでこれを使うと「対話的」に選択肢から選ぶだけで簡単にCommitメッセージを書くことができるので必須

Commitする際の流れ

  1. git commit時にHuskyでCommitlintを毎回呼び出す
  2. Commitlintは決められたルールに基づいてるかを確認

上記の流れを「git-cz」で楽にしていきます。

harrythecodeharrythecode

事前準備

検証環境

  • OS: macOS Monterey v12.4 - M1 Pro
  • software:
$ npm -v
6.14.16
$ node -v
v14.19.1

npmプロジェクトの作成

  1. 適当なフォルダ内でnpm init を実行
作業ログ
$ mkdir apply-commit-rules
$ cd apply-commit-rules
$ npm init
package name: (apply-commit-rules)
version: (1.0.0)
description: Example of how to apply your commit rules on Git system
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to ~/Workspace/apply-commit-rules/package.json:

{
  "name": "apply-commit-rules",
  "version": "1.0.0",
  "description": "Example of how to apply your commit rules on Git system ",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Is this OK? (yes) yes

テスト用なので中身は何でも良いです。当該フォルダ内で今後のコマンドを実行していきます。

harrythecodeharrythecode

Huskyの設定

🚨【気をつけること】
執筆時点での最新は「v.8.0.1」(npm)です。
バージョンによっては手順が異なるので必ず公式サイトの手順を確認するようにしましょう。

  1. huskyをインストール
npm install husky --save-dev
  1. huskyに必要なフォルダ(.husky)の作成
npx husky install

Commitlintと連携するのでこれで十分です。次に進みます。

Commitlintの設定

📚【マメ知識】
設定ファイルの記述方法が複数あります。
例えば、package.jsonに記述する方法もあり、ややこしいので今回は公式が載せている手順に従います。

  1. Commitlintをインストール
npm install @commitlint/cli @commitlint/config-conventional --save-dev
  1. 設定ファイルを作成
cat <<EOT > commitlint.config.js
module.exports = {
    extends: ['@commitlint/config-conventional'],
    // https://commitlint.js.org/#/reference-rules
    rules: {
        'subject-case': [2, 'always', 
            [
                'lower-case', // default
                'upper-case', // UPPERCASE
                'camel-case', // camelCase
                'kebab-case', // kebab-case
                'pascal-case', // PascalCase
                'sentence-case', // Sentence case
                'snake-case', // snake_case
                'start-case' // Start Case
            ]
        ],
    },
}
EOT

デフォルトルールが厳しめの為、少しゆるくします。

  1. 実際に動くか確認
echo 'foo: bar' | npx commitlint
⧗   input: foo: bar
✖   type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]

✖   found 1 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
harrythecodeharrythecode

HuskyとCommitlintの連携

  1. husky/commit-msgファイルを作成し、commitlintのコマンドを追加する
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
  1. husky/commit-msgファイルが作成されてることを確認します (e.g., cat .husky/commit-msg)
.husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit "$1"

これでgit commitコマンドを実行した際に「npx commitlint」コマンドが実行されます。以下は例です。

コマンド実行例
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.husky/
	commitlint.config.js
	package-lock.json
	package.json

$ git add .
$ git commit -m "Test"
⧗   input: Test
✖   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

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

これだけでも実務で使えなくはないですが、全てのルールに沿ったコメントを記憶するのは非常に疲れるので楽になるツールを導入します。

harrythecodeharrythecode

git-czの設定

  1. git-cz をインストール
npm install --save-dev git-cz
  1. コマンドが実行できるかを確認。「Ctrl + c」で終了させてください。
$ npx git-cz
? Select the type of change that you're committing: (Use arrow keys or type to search)
❯ 💍  test:       Adding missing tests
  🎸  feat:       A new feature
  🐛  fix:        A bug fix
  🤖  chore:      Build process or auxiliary tool changes
  ✏️  docs:       Documentation only changes
  💡  refactor:   A code change that neither fixes a bug or adds a feature
  💄  style:      Markup, white-space, formatting, missing semi-colons...
(Move up and down to reveal more choices)

デフォルト設定のままだと絵文字がcommitメッセージに含まれるので無効化します。

  1. package.jsonファイルのscriptsを以下のように書き換えます。
package.json
  "scripts": {
    "commit": "git-cz --disable-emoji"
  },
  1. npm run commitで先ほど設定したscriptsコマンドを実行できます。
作業ログ
$ npm run commit

> apply-commit-rules@1.0.0 commit ~/Workspace/apply-commit-rules
> git-cz --disable-emoji

? Select the type of change that you're committing: feat:       A new feature
? Write a short, imperative mood description of the change:
  [-------------------------------------------------------------] 55 chars left
   feat: this is a new feature
? Provide a longer description of the change:
  Here is my longer description
? List any breaking changes
  BREAKING CHANGE:
? Issues this commit closes, e.g #123:
[main 9e727d1] feat: this is a new feature
 4 files changed, 1606 insertions(+)
 create mode 100755 .husky/commit-msg
 create mode 100644 commitlint.config.js
 create mode 100644 package-lock.json
 create mode 100644 package.json
$ git push

これで一通りの設定は完了しました。後は各現場で使いやすいようにルールを調整していけばOKです。

今回のコードは全て以下のレポジトリに配置しています。

harrythecodeharrythecode

最後に補足

  • デフォルトのcommitlintのルールがあまりにも厳しすぎるので何か別のに変えるべきかも→以下のように変更
commitlint.config.js
module.exports = {
    extends: ['@commitlint/config-conventional'],
    // https://commitlint.js.org/#/reference-rules
    rules: {
        'subject-case': [2, 'always', 
            [
                'lower-case', // default
                'upper-case', // UPPERCASE
                'camel-case', // camelCase
                'kebab-case', // kebab-case
                'pascal-case', // PascalCase
                'sentence-case', // Sentence case
                'snake-case', // snake_case
                'start-case' // Start Case
            ]
        ],
    },
}
このスクラップは2022/09/07にクローズされました