LoginSignup
96
43

husky + lint-stagedの支配から解放されるLefthookという選択肢

Last updated at Posted at 2023-12-24

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が生成されます。

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に書き直すだけなのでとても簡単です。

package.json
// Before
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,jsx}": "eslint"
  }
}
lefthook.yml
# 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 run option or provide your custom files command.

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} - custom files command 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 from lefthook.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 root option.

This is useful when you execute some npm or yarn command but the package.json is in another directory.

For pre-push and pre-commit hooks and for the custom files command root option is used to filter file paths. If all files are filtered the command will be skipped.

rootオプションを使うことでコマンドを実行するCWDを指定できるため、Monorepoそれぞれに対応した設定を書くことができます。

実際の動作確認

lefthook.yml
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_error.gif

ramen配下のファイルのみがLintチェックされ、sushi配下では対象ファイルがないためスキップされています。

root glob runの3つのオプションを組み合わせることで、Monorepo環境での設定ができそうですね。

まとめ

husky+lint-stagedの代わりにLefthookでGit hooksを管理する方法を紹介しました。

husky+lint-stagedと異なり、Node.jsに依存しない環境でも使えるので、バックエンドのプロジェクトでも利用しやすいのではないでしょうか。

Monorepo環境での設定も簡単にできるので、ぜひ試してみてください!

PR

HRBrainでは一緒に働く仲間を募集しています。歴史に残るトライをしよう!

96
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
96
43