モチベーション
Gitリポジトリにコミットするソースコードの質を保つためにhusky と lint-stagedを組み合わせて、 gitのpre-commitイベント で リンターやフォーマッターを適用します。
.git/hooks/pre-commit
ファイルがないのに、動作しているのが気になっていたので、動作の仕組みを調べた結果です。
3行まとめ
- huskyが動作しているリポジトリで
git config -l |grep core.hookspath
を見ると、 Git の フックスクリプトのパスが .husky ディレクトリになっている。 - Gitのフックスクリプト .husky/pre-commit があり、 pre-commitで
yarn lint-staged
を実行している -
package.json ファイルの
lint-staged
(https://github.com/okonet/lint-staged#packagejson-example) に実行するリンターやフォーマッターを定義している
仕組み
動作確認に使用したバージョン
- Node.js: 14.18.3
- yarn: 1.22.17
1. Gitのフックスクリプト
Gitリポジトリの操作で コミット前、コミット後、リベース前などのイベントが発生した時にカスタムスクリプトを実行するための仕組みです。コミットに注目すると、下の4つのイベントがあります。
- pre-commit: コミットメッセージが入力される前に、コードの検査などを行う。フックスクリプトが0ではない値を返すと、コミットが中断される。
- prepare-commit-msg: コミットメッセージのデフォルトメッセージが作成され、エディタが立ち上がる直前に実行される。コミットメッセージの自動生成などに利用できる。
- commit-msg: コミットメッセージを入力して、エディタがコミットメッセージを一時ファイルに保存した後に実行される。「issueの番号が必須」等のコミットメッセージの検査に利用できる。フックスクリプトが0ではない値を返すと、コミットが中断される。
- post-commit: コミットの手続きが全て完了した後に実行される。通知などに利用できる。
2. Gitのフックスクリプトをhuskyで管理する
huskyの導入時や husky導入済みのプロジェクトで npmのパッケージをインストール時に git config core.hooksPath が .husky 設定されます。
$ yarn add --dev husky
$ npx husky install
$ yarn install
git config
で値を確認することができます。
$ git config -l |grep core.hookspath
core.hookspath=.husky
また、huskyを導入したら、 npx husky install
が適宜実行できるように package.json に スクリプトを定義します。
{
"script": {
"prepare": "husky install"
}
}
3. pre-commit時に実行する lint-staged を呼び出す
Gitの pre-commit イベントの準備ができたら、呼び出される lint-staged をインストールし、huskyのフックスクリプトに lint-staged の実行を追加します。
$ yarn add --dev lint-staged
$ yarn husky add .husky/pre-commit "yarn lint-staged"
フックスクリプト .husky/pre-commit の中をみると、 lint-staged の実行が追加されていることがわかります。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged
4. lint-staged で行うことを定義する
package.json に必要とするリンターやフォーマッターの実行を定義します。今回は、 phpstan と phpcsを実行します。
{
"scripts": {
"phpstan": "php -d 'memory_limit=1024M' vendor/bin/phpstan analyze",
"phpcs": "php vendor/bin/php-cs-fixer --config=.php-cs-fixer.dist.php fix"
},
"lint-staged": {
"*.php": [
"yarn phpstan",
"yarn phpcs"
]
}
}
動くもの
GitHubにあります。