← 記事一覧に戻る

Husky v9 で pre-commit フックが動かない・スキップされる時の5つの原因と対策【lint-staged / Windows / CI】

Husky v9 で pre-commit フックが動かない問題

チームに lint や型チェックを強制するために Husky + lint-staged を導入したのに、「コミット時にフックが実行されない」「lint-staged が動かない」「CI で毎回フックエラーが出る」といった問題に悩まされるケースは多いです。

特に Husky v8 → v9 への移行や、新規メンバーがクローン後に環境構築したタイミングで問題が起きやすい。

この記事では、よくある5つの原因とそれぞれの対処法を具体的なコードとエラーメッセージとともに解説します。


よくある症状

  • git commit してもフックが実行されずそのままコミットが通る
  • husky - pre-commit hook exited with code 127 (command not found) が出る
  • lint-staged を実行したコマンドが見つからないと言われる
  • CI(GitHub Actions)でフックエラーが発生してビルドが落ちる
  • npm install 後にフックが設定されていない

原因1: v8 スタイルのフックファイルが v9 で動かない

最もよくある原因です。Husky v9 では内部構造が大きく変わり、v8 で必須だった husky.sh のソース読み込みが廃止されました。

v8 スタイルのフックファイル(動かない):

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

v9 では .husky/_/husky.sh ファイル自体が存在しないため、. "$(dirname -- "$0")/_/husky.sh" の行で以下のエラーが出てフックが失敗します。

.husky/pre-commit: 2: .husky/pre-commit: ../_/husky.sh: not found
husky - pre-commit script failed (code 127)

v9 スタイルのフックファイル(正しい書き方):

npx lint-staged

シバン(#!/usr/bin/env sh)も不要です。v9 のフックファイルはシンプルに「実行したいコマンドだけ」書けばOKです。

移行コマンド:

# v8 → v9 移行時に自動で書き換えてくれるコマンド
npx husky init

ただし husky init は既存のフックファイルを上書きするため、カスタム内容がある場合は事前にバックアップを取っておきましょう。


原因2: フックファイルのパーミッションが足りない

macOS・Linux 環境では、.husky/ 以下のスクリプトに実行権限(+x)が付いていないと動きません。

症状:

hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.

対処法:

chmod +x .husky/pre-commit
chmod +x .husky/commit-msg  # commit-msg フックを使う場合も同様

Git にパーミッション変更を記録するには:

git update-index --chmod=+x .husky/pre-commit
git add .husky/pre-commit
git commit -m "chore: fix husky hook permissions"

これをコミットしておけば、他のメンバーがクローン後にパーミッション問題で詰まることを防げます。


原因3: lint-staged のコマンドが見つからない(PATH 問題)

husky - pre-commit hook exited with code 127 (command not found)

code 127 は「コマンドが見つからない」を意味します。原因は以下のいずれかです。

原因3-a: npx が使えない

フックファイルで lint-staged とだけ書いていると、グローバルにインストールされていない場合に失敗します。

# 悪い例
lint-staged

# 良い例(npx を明示)
npx lint-staged

原因3-b: Node.js が PATH に入っていない(macOS + GUI アプリ)

Sourcetree や GitHub Desktop などの GUI Git クライアントからコミットすると、シェルの PATH が通っていないため nodenpx が見つからないことがあります。

対処法として、フックファイルの先頭で PATH を明示します:

# macOS で homebrew の Node.js を使う場合
export PATH="/opt/homebrew/bin:$PATH"
npx lint-staged

または .huskyrcpackage.jsonhusky セクションではなく、.nvmrc.node-version と合わせて以下のように書く方法もあります:

# nvm を使う場合
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
npx lint-staged

原因4: CI 環境でフックが実行されてビルドが落ちる

GitHub Actions などの CI 環境では、npm installprepare スクリプトで Husky がインストールされ、その後のコミット操作やテストでフックが実行されることがあります。

CI でフックは不要なので、HUSKY=0 環境変数でスキップできます。

GitHub Actions での設定例:

# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      HUSKY: 0  # Husky フックを無効化
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

または npm ci 時に prepare スクリプトをスキップする方法:

- run: npm ci --ignore-scripts
- run: npm test

ただし --ignore-scripts を使うとプロジェクトの他の postinstall 等も動かなくなるため、HUSKY=0 の方が副作用が少なくおすすめです。


原因5: prepare スクリプトが設定されておらず新規クローン後にフックが入らない

新メンバーがリポジトリをクローンして npm install したのに、コミット時にフックが実行されない場合、package.jsonprepare スクリプトが設定されていない可能性があります。

確認ポイント:

{
  "scripts": {
    "prepare": "husky"
  }
}

この prepare スクリプトがあることで、npm install 実行後に自動的に Husky の初期化(.git/hooks/ へのシンボリックリンク設定)が行われます。

# prepare スクリプトを追加してから再インストール
npm pkg set scripts.prepare="husky"
npm install  # これで husky の初期化が走る

よくある誤解: .husky/ ディレクトリをコミットしてあれば動く、と思いがちですが、.git/hooks/ にシンボリックリンクが貼られていないと Git はフックを実行しません。prepare スクリプトがその橋渡しをします。


lint-staged の設定が認識されない場合

フックは動いているのに lint-staged が意図した動作をしない場合、設定ファイルの場所が問題なことがあります。

lint-staged の設定は以下のいずれかで指定できます:

// package.json に直接書く(推奨)
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{js,jsx}": ["eslint --fix", "prettier --write"],
    "*.{css,scss}": ["prettier --write"]
  }
}

または独立した設定ファイル:

// .lintstagedrc.mjs
export default {
  '*.{ts,tsx,js,jsx}': ['eslint --fix', 'prettier --write'],
  '*.{css,scss,md}': ['prettier --write'],
}

注意: Husky v9 + lint-staged v15 以降、ESM 形式の設定ファイル(.lintstagedrc.mjs)でないと認識されないケースがあります。package.json に直接書く方法が最もトラブルが少なくおすすめです。


最終確認:現在の設定状態をチェックするコマンド

# フックが .git/hooks に正しくインストールされているか確認
ls -la .git/hooks/

# husky のバージョン確認
npx husky --version

# lint-staged の動作を手動でテスト(コミットせずに実行)
npx lint-staged --debug

ls -la .git/hooks/pre-commit.husky/pre-commit へのシンボリックリンクになっていれば、Husky のインストールは成功しています。

lrwxrwxrwx ... pre-commit -> ../../.husky/pre-commit

まとめ

Husky v9 で pre-commit フックが動かない原因と対策をまとめると:

原因 症状 対策
v8 スタイルのフックファイル husky.sh: not found npx husky init で再生成
パーミッション不足 hook was ignored because it's not set as executable chmod +x .husky/pre-commit
PATH が通っていない code 127 (command not found) フックで export PATH を明示
CI でフックが実行される CI ビルドが落ちる HUSKY=0 環境変数を設定
prepare スクリプト未設定 新規クローン後に動かない package.json"prepare": "husky" を追加

Husky v9 は v8 と比べてシンプルになりましたが、移行時のつまずきポイントが集中しています。特に「v8 スタイルの husky.sh ソース読み込みの削除」は見落としやすいので、v8 → v9 移行時は必ず確認しましょう。