HRBrain Advent Calendar 2023 25日目の記事です。
はじめに
こんにちは。@yug1224(Yuji Yamaguchi)です。
これまで、pre-commit/pre-push時のLint実行にhusky+lint-stagedを使っていましたが、Monorepo環境だと設定が少し複雑になってしまうので、Lefthookを試してみることにしました。
Lefthookとは
The fastest polyglot Git hooks manager out there
A Git hooks manager for Node.js, Ruby and many other types of projects.
- Fast. It is written in Go. Can run commands in parallel.
- Powerful. It allows to control execution and files you pass to your commands.
- Simple. It is single dependency-free binary which can work in any environment.
LefthookはGoで書かれたGit hooks managerです。commitやpushなど、Gitの特定イベントが発生した時に実行するカスタムスクリプトの管理ができます。
コマンドの並列実行が可能で、Node.jsやRuby、フロントエンドやバックエンドに限らず、さまざまな種類のプロジェクトに対応しています。
使い方
インストール方法はいくつかありますが、今回はHomebrewでインストールします。それ以外の方法はドキュメントを参照してください。
brew install lefthook
インストール後、lefthookコマンドが使えるようになります。
lefthook install
lefthook installを実行すると、lefthook.ymlが存在しない場合は、テンプレートとしてlefthook.ymlが生成されます。
# EXAMPLE USAGE:
#
# Refer for explanation to following link:
# https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
#
# pre-push:
# commands:
# packages-audit:
# tags: frontend security
# run: yarn audit
# gems-audit:
# tags: backend security
# run: bundle audit
#
# pre-commit:
# parallel: true
# commands:
# eslint:
# glob: "*.{js,ts,jsx,tsx}"
# run: yarn eslint {staged_files}
# rubocop:
# tags: backend style
# glob: "*.rb"
# exclude: "application.rb|routes.rb"
# run: bundle exec rubocop --force-exclusion {all_files}
# govet:
# tags: backend style
# files: git ls-files -m
# glob: "*.go"
# run: go vet {files}
# scripts:
# "hello.js":
# runner: node
# "any.go":
# runner: go run
コメントアウトされているテンプレート部分を参考にして、lefthook.ymlに必要なコマンドを記述します。
lefthook uninstall && lefthook install
lefthook.ymlの編集後は、lefthook uninstall && lefthook installを実行し、設定を反映させることも忘れないようにしましょう!
husky+lint-stagedからの移行
husky+lint-stagedからの移行方法はドキュメントにも書かれていますが、これまでpackage.jsonに書いていた設定をlefthook.ymlに書き直すだけなのでとても簡単です。
// Before
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx}": "eslint"
}
}
# After
pre-commit:
parallel: true
commands:
eslint:
glob: "*.{js,jsx}"
run: npx eslint {staged_files} && git add {staged_files}
glob
You can set a glob to filter files for your command. This is only used if you use a file template in
runoption or provide your customfilescommand.
globにはlint-stagedで設定していたパターンを記述します。
run
This is a mandatory option for a command. This is actually a command that is executed for the hook.
You can use files templates that will be substituted with the appropriate files on execution:
{files}- customfilescommand result.{staged_files}- staged files which you try to commit.{push_files}- files that are committed but not pushed.{all_files}- all files tracked by git.{cmd}- shorthand for the command fromlefthook.yml.{0}- shorthand for the single space-joint string of git hook arguments.{N}- shorthand for the N-th git hook argument.
runにはlint-stagedで設定していたコマンドを記述します。
{staged_files}はGitでステージングされたファイルのリストで、globによってマッチしたファイルのみが対象になります。
git commit時に、globでマッチしたファイルをnpx eslintでLintチェックし、git addでステージングします。これでhusky+lint-stagedと同じように動作します。
今回はpre-commitを例に挙げましたが、pre-pushなども同様に設定できます。
Monorepoでの便利な使い方
LefthookはMonorepo環境での利用にも対応しています。
root
You can change the CWD for the command you execute using
rootoption.This is useful when you execute some
npmoryarncommand but thepackage.jsonis in another directory.For
pre-pushandpre-commithooks and for the customfilescommandrootoption is used to filter file paths. If all files are filtered the command will be skipped.
rootオプションを使うことでコマンドを実行するCWDを指定できるため、Monorepoそれぞれに対応した設定を書くことができます。
実際の動作確認
pre-commit:
parallel: true
commands:
ramen:
root: ramen/
glob: '*.{ts,tsx}'
run: npx eslint --fix {staged_files} && git add {staged_files}
sushi:
root: sushi/
glob: '*.{ts,tsx}'
run: npx eslint --fix {staged_files} && git add {staged_files}
実際に動作するサンプルを作ってみました。ramenディレクトリとsushiディレクトリに対応する設定を書いています。
ramen配下のファイルを壊した状態でgit commitをするとどうなるか確認してみましょう。
ramen配下のファイルのみがLintチェックされ、sushi配下では対象ファイルがないためスキップされています。
root glob runの3つのオプションを組み合わせることで、Monorepo環境での設定ができそうですね。
まとめ
husky+lint-stagedの代わりにLefthookでGit hooksを管理する方法を紹介しました。
husky+lint-stagedと異なり、Node.jsに依存しない環境でも使えるので、バックエンドのプロジェクトでも利用しやすいのではないでしょうか。
Monorepo環境での設定も簡単にできるので、ぜひ試してみてください!
PR
HRBrainでは一緒に働く仲間を募集しています。歴史に残るトライをしよう!
