monorepo環境でのgit hooksの管理でどう設定するのがいいのか、まとまった資料はなさそうなので書き記します。
以降はhuskyとlint-staged、lefthookについては知っている前提です。 知らない場合はざっくり説明すると
husky → git hooksを管理するライブラリ
lint-staged → gitのstagedなファイルに対して任意のコマンドを実行できるライブラリ
lefthook → go製のgit hooks管理ライブラリ
個人的には後述する理由からlefthook推奨です。
前提
バージョンは以下のとおりです。
husky: 8.0.1 lint-staged: 13.0.3 lefthook: 1.1.1
また、今回はgoとnext.jsのアプリケーションが横並びである状態を想定しています。
$ tree -L 1 . ├── go └── next
目標は、commit時に各アプリケーションのファイルごとに特定のコマンドを実行するようにします。
jsやts、tsxファイルなどに対してはeslintやprettier、goファイルに対してはgo fmt
やgolangcil-intを実行します。
husky+lint-stagedでの設定
まずroot directoryで以下コマンドを実行します。
# package.jsonを作成 yarn init # 依存ライブラリをinstall yarn add -D husky lint-staged # package.jsonにhuskyの初期化コマンド追加 npm set-script prepare "husky install" # huskyの初期化 yarn prepare # git hooksにpre-commit時のコマンド yarn husky add .husky/pre-commit "yarn lint-staged"
これでcommit時にlint-stagedが実行されます。
ではアプリケーションごとに実行アプリケーションごとに実行させるには各アプリケーションのディレクトリごとに.lintstagedrc
を追加します。
# root/next/.lintstagedrc.js const path = require('path') module.exports = { '*.{js,ts,tsx}': (absolutePaths) => { const cwd = process.cwd() const relativePaths = absolutePaths.map((file) => path.relative(`${cwd}/next`, file)).join(' ') return [`eslint ${relativePaths}`, `prettier --write ${relativePaths}`] }, }
# root/go/.lintstagedrc.js module.exports = { "*.go": "go fmt" }
これで各アプリケーションごとにcommit時に指定したコマンドが実行されます。
pushとコマンドを分けたい場合は別名でconfigファイルを作成して-c
オプションで指定すれば良いです。
lefthookでの設定
lefthookはnpmのpackageはdeprecatedになっているため、macであればhomebrew経由やgoバイナリをdownloadするのが良いです。
lefthookをdownloadしたら以下コマンドを実行してlefthook.ymlを作成します。
lefthook install
そしてpre-commitに実行したいコマンドを追加します。
root
にアプリケーションのディレクトリを指定することで、そのディレクトリからコマンド実行とファイルパスの解決をしてくれます。
{staged_files}
でstagedなファイルの全件を半角スペース区切りで渡してくれます。
pre-commit: parallel: true commands: eslint: root: 'next/' glob: '**/*.{js,ts,jsx,tsx}' run: yarn eslint {staged_files} gofmt: root: 'go/' glob: '**/*.go' run: go fmt {staged_files}
そしてpre-commit設定を追加します。
lefthook add pre-commit
これでcommit時に各ファイルに対して任意のコマンドを実行してくれます。
どちらが良いか
lefthookのwikiにも記載がありますが、以下の理由から自分はlefthookを推奨します。
- huskyはNodejsランタイムが必要だがlefthookはシングルバイナリのみで動く
- huskyは並列実行できないがlefthookは並列実行可能
- lefthookは一つの設定に集約可能
- lefthookはgit hooks管理ライブラリではなくタスクランナーの側面もあるので適用範囲が広い
4番目は私見ですが、commitを契機とするタスク実行をしてくれるライブラリなのでhusky+lint-stagedよりも適用範囲は広いと思います。 任意のscriptを実行でき、dockerにも対応しているのでそもそもできることの幅がhusky+lint-stagedとはだいぶ違うという認識です。
終わりに
どちらでも基本的なmonorepoでのpre-commitの設定はできることがわかりました。
husky+lint-stagedがデファクトスタンダードですが、まだ使ったことが無い方はlefthookの導入も検討してみてください。