husky + lint-staged が動かない・コミット時に実行されない
プロジェクトにコードフォーマットや lint を自動実行するために husky + lint-staged を導入したものの、git commit 時にフックが全く動かないケースは非常によくあります。
hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.
あるいはエラーなく pass してしまう、という問題も多いです。特に husky v9 で大きな破壊的変更があったため、古い記事の設定をそのままコピーすると動かないことが増えています。
原因1:husky v9 でセットアップ手順が変わった
husky v8 までは package.json に以下の設定が必要でした:
{
"scripts": {
"prepare": "husky install"
}
}
husky v9 ではこの husky install は廃止されました。代わりに初期化コマンドが変わっています。
v8(旧)と v9(新)の比較
| 項目 | v8 | v9 |
|---|---|---|
| 初期化コマンド | npx husky install |
npx husky init |
| prepare スクリプト | "prepare": "husky install" |
"prepare": "husky" |
| フックファイルの形式 | husky.sh を source する形式 |
直接実行スクリプト |
v9 でのセットアップ:
npx husky init
これで .husky/pre-commit が作成され、package.json の scripts に "prepare": "husky" が自動追加されます。
フックの内容(.husky/pre-commit)はシンプルに:
npx lint-staged
v8 の _/husky.sh を source している古い形式は v9 では動きません。
# NG: v8 の形式(v9 では無視される)
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
# OK: v9 の形式
npx lint-staged
既存プロジェクトを v8 から v9 にアップグレードした場合、.husky/_/husky.sh の source 行を削除するだけで動くようになることが多いです。
原因2:フックファイルに実行権限がない
Unix 系の環境(macOS、Linux)では、フックファイルに実行権限がないと Git が無視します。
hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.
対処法:
chmod +x .husky/pre-commit
ただし、Git はファイルの実行ビット変更を通常の git add では追跡しません。リポジトリに反映するには git update-index が必要です:
git update-index --chmod=+x .husky/pre-commit
git add .husky/pre-commit
git commit -m "fix: make husky hook executable"
チームで共有するリポジトリでは、clone 直後に実行権限が失われることがあるため、prepare スクリプトで確実に付与するようにしましょう:
{
"scripts": {
"prepare": "husky && chmod +x .husky/*"
}
}
原因3:pnpm / Bun 環境での設定が違う
pnpm や Bun を使っている場合、prepare スクリプトの動作が npm と異なります。
pnpm での husky セットアップ
pnpm dlx husky init
pnpm v7 以降では .npmrc に以下の設定を追加しないと pnpm install 時に prepare が実行されません:
# .npmrc
enable-pre-post-scripts=true
また、CI 環境では pnpm install --frozen-lockfile に加えて --ignore-scripts を使うケースがあり、その場合は prepare がスキップされます。これ自体は正常動作ですが、意図せずスキップされている場合は注意が必要です。
Bun での husky セットアップ
bunx husky init
.husky/pre-commit 内では npx の代わりに bunx を使うと Bun 環境での実行が安定します:
# .husky/pre-commit
bunx lint-staged
Bun は bun install 実行時に prepare スクリプトを自動実行するため、基本的に npm と同じ感覚で使えます。
原因4:lint-staged の設定が認識されない・パターンが間違っている
lint-staged の設定は package.json または lint-staged.config.mjs などで指定しますが、glob パターンが間違っていると対象ファイルがゼロになりサイレントにスキップされます。
よくある設定ミス
// NG: src/ 配下しか対象にならない
{
"lint-staged": {
"src/**/*.{ts,tsx}": "eslint --fix"
}
}
// OK: プロジェクト全体の .ts ファイルを対象に
{
"lint-staged": {
"**/*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"**/*.{json,md,css}": "prettier --write"
}
}
lint-staged のパターンはステージングされたファイルに対してマッチします。src/ プレフィックスが付いているとルート直下の .ts ファイルや別ディレクトリのファイルがヒットしません。
lint-staged の詳細ログで確認する
npx lint-staged --verbose
--verbose フラグで対象ファイルの一覧と実行されたコマンドが表示されます。対象ファイルが 0 件の場合、パターンの見直しが必要です。
eslint --fix 後のスタッシュ問題
lint-staged は ESLint の --fix 実行後に変更ファイルを自動で git add します。しかし、部分的にステージングされたファイル(一部だけ git add した場合)があると、lint-staged の stash 処理が失敗することがあります:
✖ lint-staged failed due to a git error.
Any lost modifications can be restored from a git stash.
この場合は git stash list で退避された変更を確認し、git stash pop で復元できます。
原因5:CI 環境でエラーになる(HUSKY=0 が必要)
CI(GitHub Actions など)で npm ci を実行すると prepare スクリプト経由で husky が起動しますが、CI 環境は Git リポジトリのフルクローンでない場合があり以下のエラーが出ます:
Not a git repository (or any of the parent directories): .git
または husky 自体のエラー:
husky - HUSKY env variable is set to "0", skipping install
対処法:CI 環境では HUSKY=0 環境変数を設定してスキップします。
GitHub Actions の例:
# .github/workflows/ci.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
env:
HUSKY: 0
または package.json の prepare スクリプトで CI を検出して条件分岐する方法:
{
"scripts": {
"prepare": "node -e "if(!({}).CI) process.exit(require('child_process').spawnSync('husky',{stdio:'inherit'}).status)""
}
}
動作確認の手順
設定後、以下で動作確認してください:
# 1. Git が hooks パスを認識しているか確認
git config core.hooksPath
# → .husky と表示されればOK
# 2. フックファイルの実行権限確認
ls -la .husky/pre-commit
# → -rwxr-xr-x になっていればOK
# 3. フックを手動実行してテスト(コミットなし)
bash .husky/pre-commit
# 4. lint-staged のデバッグ実行
npx lint-staged --verbose --debug
git config core.hooksPath が何も返さない場合、npx husky init が正しく実行されていません。
まとめ
| 症状 | 原因 | 対処法 |
|---|---|---|
| コミット時に何も実行されない | v9 で初期化手順が変わった | npx husky init で再セットアップ |
| 「ignored because not executable」 | 実行権限がない | chmod +x .husky/pre-commit |
| pnpm install 後に動かない | prepare が実行されない |
.npmrc に enable-pre-post-scripts=true |
| CI でエラーになる | Git リポジトリがない環境で husky が起動 | HUSKY=0 環境変数を設定 |
| lint-staged がスキップされる | glob パターンが間違っている | --verbose で対象ファイルを確認 |
husky v8 → v9 への移行では特に「古い形式の _/husky.sh を source している行を削除する」という作業を忘れがちです。CI 環境での HUSKY=0 も合わせて設定しておくと、チーム全体でスムーズに運用できます。
