lefthookで実現する高速なpre-commit hook
lefthookで実現する高速なpre-commit hook¶
コミット前に自動でlint・format・型チェックを実行し、品質の低いコードがリポジトリに混入するのを防ぐ。それがpre-commit hookである。
なぜこれを学ぶのか¶
チーム開発では、コードの品質を一定に保つことが重要である。しかし、人間は忘れる。「npm run lint」を実行し忘れてコミットしてしまうことは珍しくない。
pre-commit hookを導入すれば、コミット前に自動でチェックが走る。人間が忘れても、機械が忘れない。
pre-commit hookとは¶
Gitには「hook」という仕組みがある。特定のタイミングで自動的にスクリプトを実行できる機能である。
| hook名 | 実行タイミング | 用途 |
|---|---|---|
| pre-commit | コミット前 | lint、format、型チェック |
| pre-push | プッシュ前 | ビルド確認、テスト |
| commit-msg | コミットメッセージ作成後 | メッセージ形式のチェック |
pre-commit hookでチェックに失敗すると、コミットがキャンセルされる。問題のあるコードがリポジトリに入ることを防げる。
なぜlefthookを選ぶのか¶
Git hook管理ツールはいくつかある。
| ツール | 特徴 |
|---|---|
| Husky | 定番。npmエコシステムで広く使われている |
| lefthook | 高速・軽量。設定がシンプル |
| simple-git-hooks | 最小構成。機能は限定的 |
| .git/hooks直接 | 依存なし。チーム共有が面倒 |
lefthookを選ぶ理由は以下の通り。
| 観点 | lefthook | Husky |
|---|---|---|
| 速度 | 高速(Go製) | 普通 |
| 設定 | YAMLで簡潔 | 複数ファイルが必要 |
| lint-staged | 内蔵 | 別途インストール |
| 依存 | 単体で完結 | lint-stagedが別途必要 |
環境¶
| 項目 | バージョン |
|---|---|
| Node.js | 20.x |
| lefthook | 2.x |
| ESLint | 9.x |
| Prettier | 3.x |
| TypeScript | 5.x |
インストール¶
npm install -D lefthook prettier
lefthookをインストールしたら、Git hookを有効化する。
npx lefthook install
このコマンドで .git/hooks/ にスクリプトが生成される。
設定ファイル¶
プロジェクトルートに lefthook.yml を作成する。
# lefthook.yml
pre-commit:
parallel: true
commands:
lint:
glob: "*.{js,ts,jsx,tsx}"
run: npx eslint {staged_files} --fix
stage_fixed: true
typecheck:
run: npx tsc --noEmit
format:
glob: "*.{js,ts,jsx,tsx,json,css,md}"
run: npx prettier --write {staged_files}
stage_fixed: true
pre-push:
commands:
build:
run: npm run build
設定の解説¶
| 項目 | 説明 |
|---|---|
parallel: true |
各コマンドを並列実行。高速化に寄与 |
glob |
対象ファイルのパターン。マッチしたファイルのみ処理 |
{staged_files} |
ステージングされたファイルのみを対象にする変数 |
stage_fixed: true |
自動修正されたファイルを再ステージング |
pre-commitで実行する内容¶
| コマンド | 目的 |
|---|---|
| lint | ESLintでコード品質をチェック。自動修正も行う |
| typecheck | TypeScriptの型エラーをチェック |
| format | Prettierでコードフォーマット |
pre-pushで実行する内容¶
| コマンド | 目的 |
|---|---|
| build | ビルドが通るか確認。壊れたコードをpushしない |
Prettier設定¶
.prettierrc を作成する。
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80
}
.prettierignore も作成する。
node_modules
.next
out
build
storybook-static
*.log
.env*
package.jsonの設定¶
他の開発者がリポジトリをクローンしたときに自動でhookが有効になるよう、prepareスクリプトを追加する。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"lint": "eslint",
"lint:fix": "eslint --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"typecheck": "tsc --noEmit",
"prepare": "lefthook install"
}
}
prepareはnpm installの後に自動実行される。これにより、チームメンバーがリポジトリをクローンしてnpm installするだけで、hookが有効になる。
動作確認¶
コミットしてみると、lefthookが動作する。
git add .
git commit -m "test commit"
以下のような出力が表示される。
╭───────────────────────────────────────╮
│ 🥊 lefthook v2.0.15 hook: pre-commit │
╰───────────────────────────────────────╯
────────────────────────────────────
summary: (done in 3.06 seconds)
✔️ format (1.16 seconds)
✔️ typecheck (2.10 seconds)
✔️ lint (3.01 seconds)
すべてのチェックが通ればコミットが完了する。失敗するとコミットがキャンセルされる。
エラー時の挙動¶
lintエラーがあると、以下のように表示される。
╭───────────────────────────────────────╮
│ 🥊 lefthook v2.0.15 hook: pre-commit │
╰───────────────────────────────────────╯
────────────────────────────────────
summary: (done in 2.50 seconds)
✔️ format (0.98 seconds)
✔️ typecheck (1.80 seconds)
❌ lint (2.50 seconds)
src/app/page.tsx
10:5 error 'unused' is defined but never used @typescript-eslint/no-unused-vars
エラーを修正してから再度コミットする必要がある。
hookをスキップする方法¶
緊急時など、hookをスキップしたい場合は --no-verify オプションを使う。
git commit --no-verify -m "emergency fix"
ただし、これは最終手段である。チームで運用する場合は、hookをスキップする理由を明確にすべきである。
なぜpre-pushでビルド確認をするのか¶
pre-commitではなくpre-pushでビルド確認を行う理由は、速度と頻度のバランスである。
| hook | 頻度 | 許容時間 |
|---|---|---|
| pre-commit | 高い(1日に何度も) | 数秒〜10秒程度 |
| pre-push | 低い(1日に数回) | 数十秒〜数分 |
ビルドは時間がかかることが多い。毎コミットで実行すると開発体験が悪化する。pushの前に1回だけ確認すれば十分である。
CI/CDとの役割分担¶
pre-commit hookとCI/CDは補完関係にある。
| 項目 | pre-commit hook | CI/CD |
|---|---|---|
| 実行場所 | ローカル | サーバー |
| 目的 | 早期フィードバック | 最終ゲートキーパー |
| スキップ可能 | --no-verifyで可能 |
不可(設定による) |
| 対象 | ステージングされたファイル | 全ファイル |
pre-commit hookは「開発者への早期フィードバック」が目的である。CI/CDは「リポジトリへの最終的な品質保証」が目的である。
理想的な構成は以下の通り。
(lint, format, typecheck)"] B --> C["commit"] C --> D["pre-push
(build)"] D --> E["push"] E --> F["CI/CD
(lint, test, build, deploy)"]
まとめ¶
- pre-commit hookは、コミット前に自動でコード品質をチェックする仕組み
- lefthookはHuskyより高速で、設定がシンプル
- lint-stagedの機能が内蔵されており、ステージングされたファイルのみを対象にできる
- pre-commitでlint・format・typecheckを実行し、pre-pushでビルド確認を行う
- CI/CDと組み合わせることで、多層的な品質保証が実現できる
用語¶
| 用語 | 説明 |
|---|---|
| Git hook | Gitの特定のイベント(commit, pushなど)で自動実行されるスクリプト |
| pre-commit | コミット前に実行されるhook |
| pre-push | プッシュ前に実行されるhook |
| lint | コードの品質や一貫性をチェックするツール |
| format | コードの書式を統一するツール |
| staged files | git addでステージングされたファイル |