Documentation Next.js

はじめに

Git Hooksを活用することで、コミット前のコード品質チェックやコミットメッセージの規約強制を自動化できます。本記事では、Husky v9を使った最新のセットアップ方法を解説します。

パッケージのインストール

# Husky v9
npm install -D husky

# lint-staged(ステージファイルのみチェック)
npm install -D lint-staged

# commitlint(コミットメッセージ検証)
npm install -D @commitlint/cli @commitlint/config-conventional

Husky v9セットアップ

初期化

npx husky init

このコマンドで以下が実行されます:

  • .husky/ディレクトリの作成
  • .husky/pre-commitファイルの作成
  • package.jsonprepareスクリプトを追加

pre-commitフック

# .husky/pre-commit
npx lint-staged

commit-msgフック

# .husky/commit-msgを作成
echo 'npx --no -- commitlint --edit "$1"' > .husky/commit-msg

pre-pushフック

# .husky/pre-pushを作成
echo 'npm run typecheck && npm run test' > .husky/pre-push

lint-staged設定

package.jsonに設定

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write"
    ],
    "*.{js,jsx,mjs,cjs}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,scss,md,mdx}": [
      "prettier --write"
    ]
  }
}

別ファイルで設定(.lintstagedrc.js)

// .lintstagedrc.js
const path = require('path');

const buildEslintCommand = (filenames) =>
  `next lint --fix --file ${filenames
    .map((f) => path.relative(process.cwd(), f))
    .join(' --file ')}`;

module.exports = {
  '*.{ts,tsx}': [
    buildEslintCommand,
    'prettier --write',
  ],
  '*.{js,jsx,mjs}': [
    'eslint --fix',
    'prettier --write',
  ],
  '*.{json,css,scss,md}': [
    'prettier --write',
  ],
};

commitlint設定

commitlint.config.js

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // type(スコープ): 説明 の形式
    'type-enum': [
      2,
      'always',
      [
        'feat',     // 新機能
        'fix',      // バグ修正
        'docs',     // ドキュメント
        'style',    // フォーマット(コードの動作に影響しない)
        'refactor', // リファクタリング
        'perf',     // パフォーマンス改善
        'test',     // テスト追加・修正
        'build',    // ビルド関連
        'ci',       // CI設定
        'chore',    // その他の変更
        'revert',   // コミットの取り消し
      ],
    ],
    'type-case': [2, 'always', 'lower-case'],
    'subject-case': [0], // 日本語対応のため無効化
    'subject-empty': [2, 'never'],
    'subject-max-length': [2, 'always', 100],
    'body-max-line-length': [0], // 本文の長さ制限なし
  },
};

有効なコミットメッセージ例

# OK
git commit -m "feat: ユーザー登録機能を追加"
git commit -m "fix(auth): ログインエラーを修正"
git commit -m "docs: READMEを更新"
git commit -m "refactor: コンポーネントを分割"

# NG
git commit -m "新機能追加"      # typeがない
git commit -m "FEAT: 機能追加"  # 大文字
git commit -m "feat:"          # 説明がない

型チェックの追加

pre-pushで型チェック

# .husky/pre-push
npm run typecheck
npm run test

並列実行で高速化

# .husky/pre-push(並列実行)
npm run typecheck & npm run test & wait

GitHub Actions連携

.github/workflows/ci.yml

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run ESLint
        run: pnpm lint

      - name: Check formatting
        run: pnpm format:check

  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: TypeScript type check
        run: pnpm typecheck

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run tests
        run: pnpm test

  build:
    runs-on: ubuntu-latest
    needs: [lint, typecheck, test]
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm build

PRタイトルの検証

# .github/workflows/pr-title.yml
name: PR Title Check

on:
  pull_request:
    types: [opened, edited, synchronize]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: amannn/action-semantic-pull-request@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          types: |
            feat
            fix
            docs
            style
            refactor
            perf
            test
            build
            ci
            chore
            revert
          requireScope: false
          subjectPattern: ^.{1,100}$

トラブルシューティング

Huskyが動作しない場合

# Huskyを再インストール
rm -rf .husky
npx husky init

# pre-commitを再設定
echo 'npx lint-staged' > .husky/pre-commit

Windows環境での問題

# Git Bash使用時にスクリプトが実行されない場合
git config core.hooksPath .husky

フックをスキップする場合

# 緊急時のみ使用(非推奨)
git commit --no-verify -m "fix: 緊急修正"
git push --no-verify

CI環境でHuskyを無効化

{
  "scripts": {
    "prepare": "husky || true"
  }
}

スクリプトのカスタマイズ

カスタムバリデーション

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

# 機密情報のチェック
if git diff --cached --name-only | xargs grep -l -E "(API_KEY|SECRET|PASSWORD)" 2>/dev/null; then
  echo "Error: 機密情報が含まれている可能性があります"
  exit 1
fi

# lint-staged実行
npx lint-staged

ブランチ名の検証

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

branch=$(git rev-parse --abbrev-ref HEAD)

# ブランチ名のパターンチェック
if ! echo "$branch" | grep -qE "^(main|develop|feature\/|fix\/|hotfix\/)"; then
  echo "Error: ブランチ名が規約に従っていません"
  echo "使用可能: main, develop, feature/*, fix/*, hotfix/*"
  exit 1
fi

npm run typecheck
npm run test

まとめ

フック用途実行タイミング
pre-commitlint-stagedコミット前
commit-msgcommitlintコミットメッセージ入力後
pre-push型チェック・テストプッシュ前

参考文献

円