1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HuskyとLefthookの比較

1
Posted at

はじめに

これまで、Gitフックの管理ツールは husky を使用していましたが、近年 Lefthook も注目を集めています。
今回は、両者の特徴・違いを整理し、プロジェクトに応じた使い分けの指針を調べてみました。

huskyとは

まず、husky とはどのようなものでしょうか。

husky は、Node.jsプロジェクト向けのGitフック管理ツールです。
.husky/ ディレクトリにフック用のシェルスクリプトを配置することでフックを可能にしています。

特徴

項目 内容
言語 JavaScript(Node.js必須)
設定形式 シェルスクリプト(.husky/pre-commit など)
実行方式 逐次実行
インストールサイズ 小(依存少)
コミュニティ 非常に大きい

ユースケース

  • Node.js / フロントエンドのみのプロジェクト
  • lint-stagedと組み合わせたステージファイルへのlint実行
  • チーム全員がNode.js環境を持っていることが前提のプロジェクト

Lefthookとは

Lefthook は、Go製のGitフック管理ツールです。
lefthook.yml にYAML形式で設定を記述し、単一バイナリで動作するため言語に依存しません。並列実行やフィルタリングなど高機能な設定が可能です。

特徴

項目 内容
言語 Go(Node.js不要)
設定形式 YAML(lefthook.yml
実行方式 並列実行対応
インストールサイズ バイナリ1つ(軽量・高速)
コミュニティ 成長中

ユースケース

  • 複数言語が混在するモノレポ
  • Gitフックの処理を高速化したいプロジェクト
  • Node.js環境に依存したくないバックエンドプロジェクト

導入手順

まずはそれぞれのインストール手順を見ていきましょう。

husky

インストール

npm install --save-dev husky
npx husky init

init コマンドにより .husky/ ディレクトリと pre-commit のサンプルが生成されます。

設定

package.jsonprepare スクリプトを追加します(init 実行時に自動追加されます)。

package.json
{
  "scripts": {
    "prepare": "husky"
  }
}

Lefthook

インストール

npm経由でインストールする場合:

npm install --save-dev @evilmartians/lefthook
npx lefthook install

Homebrewの場合:

brew install lefthook
lefthook install

設定

プロジェクトルートに lefthook.yml を作成します。

lefthook.yml
pre-commit:
  parallel: true
  commands:
    lint:
      glob: "*.{ts,tsx,js,jsx}"
      run: npx eslint {staged_files}
    typecheck:
      run: npx tsc --noEmit

実行例

次に実際の使用方法を比較します。

huskyの基本的な使い方

.husky/pre-commit に実行したいコマンドを記述します。

.husky/pre-commit
npx lint-staged

lint-staged と組み合わせてステージされたファイルだけにlintを実行する構成が一般的です。

package.json
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"]
  }
}

Lefthookの基本的な使い方

lefthook.yml だけで完結します。{staged_files} プレースホルダーでステージファイルを参照できます。

lefthook.yml
pre-commit:
  parallel: true
  commands:
    eslint:
      glob: "*.{ts,tsx}"
      run: npx eslint --fix {staged_files}
    prettier:
      glob: "*.{ts,tsx,css}"
      run: npx prettier --write {staged_files}

commit-msg:
  commands:
    commitlint:
      run: npx commitlint --edit {1}

応用例:Lefthookのスキップ設定

特定のコマンドをCI環境でスキップしたい場合は、環境変数で制御できます。

lefthook.yml
pre-commit:
  commands:
    lint:
      skip:
        - ref: main
      run: npx eslint {staged_files}

または実行時に個別スキップ:

LEFTHOOK_EXCLUDE=lint git commit -m "skip lint"

速度比較

LefthookはHuskyに比べ、高速であるとされています。
それには大きく2つの理由があるようです。

1. Go製シングルバイナリ

huskyはNode.jsランタイムに依存するため、フック実行のたびにNode.jsの起動コストが発生します。一方、LefthookはGoでコンパイルされたシングルバイナリであり、ランタイムの起動オーバーヘッドがありません。

2. コマンドの並列実行

huskyはデフォルトでコマンドを逐次実行します。
Lefthookは parallel: true を設定するだけでコマンドを並列実行でき、複数のlintやチェックを同時に走らせることができます。

lefthook.yml
pre-commit:
  parallel: true   # ESLint・Prettier・型チェックを同時実行
  commands:
    eslint:
      glob: "*.{ts,tsx}"
      run: npx eslint {staged_files}
    prettier:
      glob: "*.{ts,tsx,css}"
      run: npx prettier --write {staged_files}
    typecheck:
      run: npx tsc --noEmit

huskyで同等の並列実行を実現するには lint-stagedconcurrently などの追加パッケージが必要です。

実測値について

現時点では公開されている詳細なベンチマーク数値は少ないものの、実際に計測された方の記事を見ると、明らかに速度に差があります。特にチェック項目が多いプロジェクトほど、並列実行の恩恵が大きくなるようです。

huskyとLefthookの比較まとめ

観点 husky Lefthook
必要環境 Node.js必須 不要(バイナリ単体)
設定のシンプルさ シェルスクリプトで直感的 YAMLで一元管理
並列実行 非対応(lint-staged等で補完) ネイティブ対応
実行速度 普通 高速(Go製 + 並列実行)
言語非依存性 低い 高い
エコシステム 非常に充実 成長中
モノレポ対応 設定が煩雑になりやすい 得意

huskyからLefthookへの移行

ここではHuskyからLefthookの移行手順を見ていきます。

移行手順

# 1. huskyをアンインストール
npm uninstall husky

# 2. .huskyディレクトリを削除
rm -rf .husky

# 3. package.jsonのprepareスクリプトを削除
#    "prepare": "husky" を削除する

# 4. Lefthookをインストール
npm install --save-dev @evilmartians/lefthook

# 5. 【重要】huskyが設定したhooksPathをリセット
git config --unset core.hooksPath

# 6. Lefthookをインストール(.git/hooksにフックを配置)
npx lefthook install

移行時の落とし穴:core.hooksPath の問題

huskyを npx husky init で初期化すると、.git/config に以下の設定が書き込まれます。

[core]
    hooksPath = .husky/_

この設定が残ったまま lefthook install を実行すると、Lefthookのフックスクリプトが .husky/_ 以下に配置されてしまい、正常に動作しません。
必ず git config --unset core.hooksPath でリセットしてから lefthook install を実行してください。

lefthook-local.yml による個人設定の共存

Lefthookの便利な機能として、プロジェクト設定と個人設定を共存させる仕組みがあります。

lefthook-local.yml を作成すると、チームの lefthook.yml の設定を個人的にオーバーライドできます。
このファイルを .gitignore に追加しておけば、個人の設定がリポジトリに影響しません。

lefthook-local.yml
# 個人の開発環境でだけ typecheck をスキップする
pre-commit:
  commands:
    typecheck:
      skip: true
.gitignore
lefthook-local.yml

huskyでは git commit --no-verify で全フックをスキップするしかありませんでしたが、Lefthookではコマンド単位での制御が可能です。

モノレポでの設定

Lefthookは root オプションでサブパッケージのディレクトリを指定できます。

lefthook.yml
pre-commit:
  parallel: true
  commands:
    lint-frontend:
      root: "packages/frontend/"
      glob: "*.{ts,tsx}"
      run: npx eslint {staged_files}
    lint-backend:
      root: "packages/backend/"
      glob: "*.go"
      run: golangci-lint run {staged_files}

huskyでモノレポに対応する場合は複数のシェルスクリプトと条件分岐が必要になりますが、Lefthookでは root オプションで簡潔に記述できます。

便利ですねぇ。

使い分けの指針

huskyを選ぶケース

  • Node.js / フロントエンド専用のプロジェクト
  • lint-staged との組み合わせがすでに定着している
  • チームメンバーが husky に慣れており、移行コストをかけたくない

Lefthookを選ぶケース

  • 複数言語が混在するプロジェクトやモノレポ
  • Gitフックの実行速度を改善したい
  • Node.jsに依存しないシンプルな構成にしたい
  • 設定をYAMLで一元管理したい

どちらも活発にメンテナンスされており、機能面での差は縮まっているようです。
とは言え速度に差はあるので、新規プロジェクトであれば Lefthook を導入すればいいのかなぁと思います。

おわりに

個人開発などの小規模プロジェクトであればどちらでもよいのかもしれませんが、大規模、モノレポのプロジェクトの場合はLefthookを入れるのが無難なのかなと思いました。

とりあえず、新規で開発する際はLefthookを入れようと思います。

それでは。

参考文献

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?