commitlint
というツールをご紹介します。
commitlint
とは、Git のコミット規約(commit convention)に従わせるための npm ツールです。ESLint のように、ルールを JavaScript で設定・開発できます。
commitlint
helps your team adhering to a commit convention. By supporting npm-installed configurations it makes sharing of commit conventions easy.
commitlint
はチームがコミット規約に従うのを助けます。npm インストールされた設定をサポートすることにより、簡単にコミット規約を共有することができます。
「コミット規約」といえば、Angular チームの規約 が有名です。そのコミットメッセージ形式は、次の通りです。
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
例えば、こちらのコミットを見てみると、規約に従っているのが分かります。
feat(currencyFilter): trim whitespace around an empty currency symbol · angular/angular.js@62743a5
feat(currencyFilter): trim whitespace around an empty currency symbol
In most locales, this won't make a difference (since they do not have
whitespace around their currency symbols). In locales where there is a
whitespace separating the currency symbol from the number, it makes
sense to also remove such whitespace if the user specified an empty
currency symbol (indicating they just want the number).
Fixes #15018
Closes #15085
Closes #15105
commitlint
も大まかにはこの形式に従っており、ルール設定によってより細かな調整が可能です。
セットアップ
では、実際に使ってみましょう。インストールガイドは公式サイトが詳しいので、そちらに従っていきます。
commitlint
インストール
まず、CLIパッケージ(@commitlint/cli
)と設定パッケージ(@commitlint/config-conventional
)をインストールします。
$ npm install --save-dev @commitlint/{cli,config-conventional}
-
@commitlint/cli
: https://npm.im/@commitlint/cli -
@commitlint/config-conventional
: https://npm.im/@commitlint/config-conventional
次に、設定ファイル(commitlint.config.js
)を作成します。
$ echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
こんな感じです。
module.exports = {
extends: ['@commitlint/config-conventional']
};
ファイル名は .commitlintrc.js
.commitlintrc.json
.commitlintrc.yml
も可能ですし、package.json
に commitlint
フィールドを定義してもよいです。これは cosmiconfig
の仕様ですね。
husky
インストール
さて、お次は husky
をインストールします。
$ npm install --save-dev husky
husky
はGitフックを設定するためのツールです。husky
の詳細については、手前味噌ですがこちらの記事をご覧ください。
コミット前に Lint を強制するなら lint-staged が便利 - Qiita
また、Gitフックについてはこちらの記事が詳しいです。
package.json
を編集し、scripts.commitmsg
を追加します。これで、コミットメッセージ編集の際に Git フックが走るようになります(これは husky
の機能です)。
{
"scripts": {
"commitmsg": "commitlint -e $GIT_PARAMS"
}
}
テスト
さあ、テストしてみましょう。
$ git commit -m "foo: this will fail"
husky > npm run -s commitmsg
⧗ input: foo: this will fail
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
✖ found 1 problems, 0 warnings
husky > commit-msg hook failed (add --no-verify to bypass)
$ git commit -m "chore: lint on commitmsg"
husky > npm run -s commitmsg
⧗ input: chore: lint on commitmsg
✔ found 0 problems, 0 warnings
規約に沿わないコミットメッセージでは、コミットできないことが確認できます。
CIでの実行
いくら Git フックでチェックしていても、git commit --no-verify
されてしまうとチェックをすり抜けられてしまいます。
また、GitHub などではブラウザ上からコミットすることも可能です。
そのようなケースでも対応できるよう、CIで commitlint
を実行する方法も載っています。
こちらは Travis CI での設定例です。@commitlint/travis-cli
というパッケージをインストールすると、node_modules/.bin/commitlint-travis
というコマンドがインストールされます。
$ npm install --save-dev @commitlint/travis-cli
language: node_js
script:
- commitlint-travis
- npm test
ルール
commitlint
には様々なルールが存在します。
例えば、標準設定の @commitlint/config-conventional
から抜粋してみます(v5.2.3 時点)。
-
type
は次のいずれかでなければならない(type-enum
ルール)。-
build
: ビルド -
ci
: CI -
chore
: 雑事(カテゴライズする必要ないようなもの) -
docs
: ドキュメント -
feat
: 新機能 -
fix
: バグフィックス -
perf
: パフォーマンス -
refactor
: リファクタリング -
revert
: コミット取り消し(git revert
) -
style
: コードスタイル -
test
: テスト
-
-
type
は小文字でなければならない(type-case
ルール)。 -
type
は必ず指定しなければならない(type-empty
ルール)。 -
scope
は小文字でなければならない(scope-case
ルール)。 -
subject
は次のいずれかの書式(case)でなければならない(subject-case
ルール)。-
sentence-case
: (例.Sentence case
) -
start-case
: (例.Start Case
) -
pascal-case
: (例.PascalCase
) -
upper-case
: (例.UPPERCASE
)
-
-
subject
は空ではいけない(subject-empty
ルール)。 - などなど…
すべてのルールの詳細は、README.md
またはソースコード(index.js
)をご覧ください。
- commitlint/README.md at v5.2.3 · marionebl/commitlint
- commitlint/index.js at v5.2.3 · marionebl/commitlint
上書き
ルールを上書きすることもできます。例えば、ベースは @commitlint/config-conventional
を採用し、scope
を独自のものにするには、commtlint.config.js
ファイルを次のように編集します。
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [2, 'always', ['web', 'job', 'api']]
}
};
定義
ルールは ルール名: [配列]
で定義します。ESLint に似ています。
'{ルール名}': [{レベル:0|1|2}, '{可否:always|never}', {値}]
ルールレベル:
-
0
: 無効 -
1
: 警告 -
2
: エラー
ルール定義の配列は、次の形式が可能です。
- プレーンな配列
- 配列を返す関数
- 配列を返す非同期関数
-
Promise
を返す関数
{
'header-max-length': [0, 'always', 72],
'header-max-length': () => [0, 'always', 72],
'header-max-length': async () => [0, 'always', 72],
'header-max-length': () => Promise.resolve([0, 'always', 72])
}
定義済みのルールの一覧については、公式ドキュメントをご覧ください。
独自ルールの共有
ESLint の "Shareable Configs" のような機能が、commitlint
にもあります。
Shareable configuration - commitlint - Lint commit messages
カスタマイズされた rules
を、npm パッケージとして公開することができます。
メリット/デメリット
今は仕事でもプライベートでも、このツールを使ってコミット規約を強制しています。個人的には、次のメリットを強く感じています。
メリット
- 習慣づけ
- わかりやすいコミットメッセージを書くことが、習慣づけされます。
- 習慣の力は大きくて、仕組みとして強制されていれば、自然と書けるようになります。
- 書けるようなると、思考もそれに順応するようになります(このコミットは「バグ修正」なのか「リファクタリング」なのかといった判断が的確になります)。
- コミットの責任範囲の明確化
- 色んな変更が混在したコミットを避けるようになり、1つのコミットが1つのことをするようになります。
- 結果、レビューがしやすくなり、レビューにかかる時間を減らせます。
- 不毛な宗教論争の回避
- (当たり前ですが)コミットメッセージの形式のブレをなくせます。
- これは他の Lint ツールにも言えることですが、機械的に修正できるものから人間の介在をなくせば、より重要なことにリソースを集中できます。
- 迅速な内容把握
- コミットのサマリーを見ただけで、簡単な内容が把握できます。
- Slack などにコミットログが流れてくる場合、普通はサマリーしか表示されないので、サマリーだけで内容把握ができるようになると、大きいです。
- 変更履歴作成の簡略化
-
lerna
やstandard-version
のような Changelog 自動生成ツールを使っている場合、コミットログから Changelog ファイルを自動生成してくれます。 - https://npm.im/lerna
- https://npm.im/standard-version
-
デメリット
反対に、デメリットは次の点です。
- 規約が厳しすぎると、チームメンバーによってバラつきが出て、最悪守られなくなります。
type
の選択(feat
かfix
か)も結局は個人の判断に委ねられてしまうので、前もって規約を十分に理解してないと、その選択にもブレが生じます。 - 結果、チームメンバーのモチベーションが低下します。所詮ツールであり銀の弾丸ではないので、チームに導入する際は前もっての準備とメンバーの理解が必要不可欠です。
使用例
commitlint
のリポジトリです。コミットログと Changelog はこのようになっています。
参考
ConventionalCommits.org なる団体も存在します。Angular のコミット規約がベースとなっています。
興味があれば、ぜひ使ってみてください!