ESLint と Husky を使ったコミット前チェック自動化

「コミット前にコードをチェックし忘れて、後からレビューで大量の指摘を受けた」「チームメンバーによってコードの品質にばらつきがある」「CI で失敗して初めて問題に気づく」といった経験はありませんか?
開発チームでコードの品質を保つためには、できるだけ早い段階で問題を発見し、修正することが重要です。そこで威力を発揮するのが、Git hooks を活用した自動化システムです。Husky と ESLint を組み合わせることで、コミット前に自動的にコード品質をチェックし、問題のあるコードがリポジトリに入り込むことを防げます。
本記事では、Husky を使った Git hooks の設定方法から、ESLint との連携、さらにはチーム開発での実践的な運用方法まで、コミット前チェック自動化の全体像を詳しく解説いたします。効率的で確実な品質保証システムを構築していきましょう。
Husky の基本セットアップ
Husky は、Git hooks を簡単に管理できるツールです。コミットやプッシュなどの Git の操作をトリガーとして、自動的にスクリプトを実行できるようになります。
Git hooks の仕組みと役割
Git hooks は、Git の特定の操作が実行されるタイミングで自動的に呼び出されるスクリプトの仕組みです。開発フローの各段階で品質チェックを自動化できます。
主要な Git hooks とその役割
# | フック名 | 実行タイミング | 主な用途 |
---|---|---|---|
1 | pre-commit | コミット前 | コード品質チェック、フォーマット |
2 | commit-msg | コミットメッセージ作成後 | メッセージ形式の検証 |
3 | pre-push | プッシュ前 | 最終テスト、型チェック |
4 | post-merge | マージ後 | 依存関係の更新確認 |
従来の Git hooks の課題
bash# 従来の方法(.git/hooks/pre-commit)
#!/bin/sh
# 実行権限の設定が必要
chmod +x .git/hooks/pre-commit
# チーム間での共有が困難
# バージョン管理対象外のため、個別設定が必要
Husky による解決
Husky を使用することで、これらの課題を解決できます:
- バージョン管理: hook スクリプトをプロジェクトファイルとして管理
- チーム共有: すべての開発者で同じ設定を自動適用
- 簡単な設定: 複雑な権限設定や手動インストールが不要
Yarn を使った Husky のインストール
プロジェクトに Husky を導入する手順を詳しく見ていきましょう。
基本パッケージのインストール
bash# Huskyの本体とlint-stagedをインストール
yarn add --dev husky lint-staged
# TypeScriptプロジェクトの場合は型定義も追加
yarn add --dev @types/node
# ESLintが未導入の場合は併せてインストール
yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
Husky の初期化
bash# Huskyの初期設定を実行
yarn husky install
# package.jsonにpostinstallスクリプトを追加(チーム開発用)
yarn pkg set scripts.postinstall="husky install"
初期化後のファイル構造
csharpproject-root/
├── .husky/
│ ├── _/
│ │ ├── .gitignore
│ │ └── husky.sh
│ └── .gitignore
├── package.json
└── yarn.lock
初期設定とフォルダ構造
Husky が作成するファイル構造と、各ファイルの役割を理解しましょう。
Husky ディレクトリの構造
bash.husky/
├── _/ # Husky内部ファイル
│ ├── .gitignore # 内部ファイルの除外設定
│ └── husky.sh # Husky実行スクリプト
├── pre-commit # pre-commitフック(手動作成)
├── commit-msg # commit-msgフック(手動作成)
├── pre-push # pre-pushフック(手動作成)
└── .gitignore # Huskyファイルのgit管理設定
package.json の設定例
json{
"name": "my-project",
"scripts": {
"postinstall": "husky install",
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"eslint": "^8.50.0",
"@typescript-eslint/parser": "^6.7.0",
"@typescript-eslint/eslint-plugin": "^6.7.0"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": ["eslint --fix", "git add"]
}
}
初回セットアップの確認
bash# Huskyが正常にインストールされているか確認
ls -la .husky/
# Git hooksディレクトリの確認
ls -la .git/hooks/
# Huskyのバージョン確認
yarn husky --version
ESLint との連携設定
Husky と ESLint を連携させることで、コミット前に自動的にコード品質をチェックできるようになります。効率的で確実なチェック体制を構築しましょう。
pre-commit フックの設定
pre-commit フックは、git commit
実行時に自動的に呼び出されるスクリプトです。ESLint によるコード検証を組み込みましょう。
基本的な pre-commit フックの作成
bash# pre-commitフックファイルを作成
yarn husky add .husky/pre-commit "yarn lint-staged"
.husky/pre-commit ファイルの内容
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# lint-stagedを実行(変更されたファイルのみチェック)
yarn lint-staged
ESLint 実行の設定パターン
bash# パターン1: 全ファイルをチェック(小規模プロジェクト向け)
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint
if [ $? -ne 0 ]; then
echo "ESLintエラーが検出されました。修正してからコミットしてください。"
exit 1
fi
# パターン2: 段階的チェック(推奨)
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🔍 コード品質をチェック中..."
# 1. 変更ファイルのみESLintチェック
yarn lint-staged
# 2. TypeScript型チェック(軽量)
echo "📝 TypeScript型チェック中..."
yarn type-check --incremental
echo "✅ すべてのチェックが完了しました"
エラーハンドリングの強化
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 色付きメッセージの設定
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "${YELLOW}🔍 コミット前チェックを開始...${NC}"
# ESLintチェック
if ! yarn lint-staged; then
echo "${RED}❌ ESLintエラーが検出されました${NC}"
echo "${YELLOW}修正方法:${NC}"
echo " yarn lint:fix # 自動修正を実行"
echo " yarn lint # エラー詳細を確認"
exit 1
fi
echo "${GREEN}✅ コード品質チェック完了${NC}"
lint-staged との組み合わせ
lint-staged は、Git でステージングされた(git add
された)ファイルのみに対してコマンドを実行するツールです。大規模プロジェクトでのパフォーマンス向上に欠かせません。
lint-staged の基本設定
json{
"lint-staged": {
// TypeScript/JavaScriptファイル
"*.{ts,tsx,js,jsx}": [
"eslint --fix", // 自動修正可能なエラーを修正
"prettier --write" // コードフォーマットを統一
],
// JSONファイル
"*.json": ["prettier --write"],
// CSSファイル
"*.{css,scss,sass}": [
"stylelint --fix",
"prettier --write"
],
// Markdownファイル
"*.md": ["prettier --write"]
}
}
高度な lint-staged 設定
json{
"lint-staged": {
// ファイルタイプ別の詳細設定
"src/**/*.{ts,tsx}": [
"eslint --fix --max-warnings 0", // 警告もエラー扱い
"jest --bail --findRelatedTests" // 関連テストを実行
],
// 特定ディレクトリでの設定分岐
"app/**/*.{ts,tsx}": [
"eslint --config .eslintrc.app.js --fix"
],
"pages/**/*.{ts,tsx}": [
"eslint --config .eslintrc.pages.js --fix"
],
// 条件付き実行
"*.{ts,tsx}": (filenames) => [
`eslint --fix ${filenames.join(' ')}`,
`tsc --noEmit --incremental`,
// 10ファイル以上の場合は全テストを実行
filenames.length > 10 ? 'yarn test' : `jest --findRelatedTests ${filenames.join(' ')}`
]
}
}
package.json スクリプトとの連携
json{
"scripts": {
// 基本的なlintコマンド
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
// lint-staged用の細分化されたコマンド
"lint:staged:js": "eslint --fix",
"lint:staged:css": "stylelint --fix",
"lint:staged:format": "prettier --write",
// デバッグ用コマンド
"lint-staged:debug": "lint-staged --debug",
"husky:test": "yarn lint-staged --dry-run"
}
}
段階的チェックの実装
開発効率とコード品質のバランスを取るため、段階的なチェック体制を構築しましょう。
3 段階チェック戦略
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🚀 段階的品質チェックを開始..."
# Stage 1: 高速チェック(必須)
echo "📋 Stage 1: 基本チェック"
if ! yarn lint-staged; then
echo "❌ ESLintエラーが検出されました"
exit 1
fi
# Stage 2: 中程度チェック(推奨)
echo "📋 Stage 2: 型チェック"
if ! yarn type-check --incremental; then
echo "❌ TypeScriptエラーが検出されました"
exit 1
fi
# Stage 3: 詳細チェック(オプション)
if [ "$HUSKY_STRICT_MODE" = "true" ]; then
echo "📋 Stage 3: 厳密チェック"
if ! yarn test --related --passWithNoTests; then
echo "❌ テストが失敗しました"
exit 1
fi
fi
echo "✅ すべてのチェックが完了しました"
環境別設定の切り替え
json{
"scripts": {
// 開発環境用(高速)
"husky:dev": "cross-env HUSKY_MODE=dev husky install",
// 本番環境用(厳密)
"husky:prod": "cross-env HUSKY_MODE=prod husky install",
// CI環境用(最適化)
"husky:ci": "cross-env HUSKY_MODE=ci husky install"
}
}
.husky/pre-commit(環境対応版)
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 環境変数に基づくチェックレベルの切り替え
case "${HUSKY_MODE:-dev}" in
"dev")
echo "🔧 開発モード: 基本チェックのみ"
yarn lint-staged
;;
"prod")
echo "🛡️ 本番モード: 厳密チェック"
yarn lint-staged && yarn type-check && yarn test --related
;;
"ci")
echo "🤖 CIモード: 最適化チェック"
yarn lint-staged && yarn type-check --incremental
;;
*)
echo "⚡ デフォルトチェック"
yarn lint-staged
;;
esac
パフォーマンス重視の設定
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 変更ファイル数に基づく動的チェック
CHANGED_FILES=$(git diff --cached --name-only | wc -l)
echo "📊 変更ファイル数: $CHANGED_FILES"
if [ "$CHANGED_FILES" -lt 5 ]; then
# 少数ファイル変更時: 厳密チェック
echo "🎯 厳密チェックモード"
yarn lint-staged && yarn type-check
elif [ "$CHANGED_FILES" -lt 20 ]; then
# 中程度変更時: 標準チェック
echo "⚖️ 標準チェックモード"
yarn lint-staged
else
# 大量変更時: 高速チェック
echo "⚡ 高速チェックモード"
yarn lint-staged --concurrent false
fi
この設定により、変更の規模に応じて適切なレベルのチェックを実行し、開発効率を維持しながら品質を保つことができます。次のセクションでは、コミットメッセージの品質管理について詳しく解説いたします。
コミットメッセージの品質管理
コードの品質だけでなく、コミットメッセージの品質も重要です。一貫性のあるコミットメッセージは、プロジェクトの履歴を理解しやすくし、自動化ツールとの連携も円滑にします。
commitlint の導入
commitlint は、コミットメッセージの形式を検証するツールです。チーム全体で統一されたメッセージ形式を保つことができます。
commitlint のインストール
bash# commitlintと設定プリセットをインストール
yarn add --dev @commitlint/cli @commitlint/config-conventional
# 日本語対応の設定を使いたい場合
yarn add --dev @commitlint/config-conventional-ja
commitlint 設定ファイルの作成
javascript// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// 日本語でのコミットメッセージを許可
'subject-case': [0],
'subject-max-length': [2, 'always', 100],
// カスタムタイプの追加
'type-enum': [
2,
'always',
[
'feat', // 新機能
'fix', // バグ修正
'docs', // ドキュメント
'style', // フォーマット
'refactor', // リファクタリング
'test', // テスト
'chore', // ビルド・設定
'perf', // パフォーマンス改善
'ci', // CI設定
'revert', // コミット取り消し
'wip', // 作業中(開発用)
],
],
// スコープの設定(プロジェクトに応じて調整)
'scope-enum': [
2,
'always',
['ui', 'api', 'auth', 'db', 'config', 'deps', 'ci'],
],
},
};
commit-msg フックの設定
bash# commit-msgフックを追加
yarn husky add .husky/commit-msg 'yarn commitlint --edit $1'
.husky/commit-msg ファイルの内容
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# commitlintでメッセージ形式をチェック
yarn commitlint --edit $1
conventional commits の適用
Conventional Commits は、コミットメッセージの標準的な形式です。自動バージョニングや CHANGELOG 生成にも活用できます。
基本的なメッセージ形式
arduino<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
具体的なコミットメッセージ例
bash# 新機能追加
feat(auth): ユーザー認証機能を追加
Google OAuth 2.0による認証機能を実装
- ログイン・ログアウト機能
- セッション管理
- 認証状態の永続化
Closes #123
# バグ修正
fix(ui): ダークモードでのボタン色を修正
ダークモード時にボタンの背景色が見えにくい問題を解決
hover時の色も調整
# 破壊的変更
feat(api)!: ユーザーAPIのレスポンス形式を変更
BREAKING CHANGE: user.name は user.fullName に変更されました
高度な commitlint 設定
javascript// commitlint.config.js(高度版)
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// ヘッダーの最大長
'header-max-length': [2, 'always', 100],
// 本文の最大行長
'body-max-line-length': [2, 'always', 100],
// フッターの最大行長
'footer-max-line-length': [2, 'always', 100],
// 空行の必須化
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [2, 'always'],
// 日本語対応
'subject-case': [0],
'subject-full-stop': [0],
// Issue番号の形式チェック
'references-empty': [2, 'never'],
// カスタムバリデーション
'custom-rules': [2, 'always'],
},
// カスタムルールの定義
plugins: [
{
rules: {
'custom-rules': ({ header }) => {
// 日本語が含まれる場合の特別な検証
const hasJapanese =
/[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/.test(
header
);
if (hasJapanese) {
// 日本語の場合は句読点をチェック
return [
!header.endsWith('。'),
'日本語のコミットメッセージは句読点で終わらないでください',
];
}
return [true];
},
},
},
],
};
メッセージテンプレートの作成
開発者がより良いコミットメッセージを書けるよう、テンプレートを提供しましょう。
Git コミットテンプレートの設定
bash# .gitmessage ファイルを作成
cat > .gitmessage << 'EOF'
# <type>[optional scope]: <description>
#
# [optional body]
#
# [optional footer(s)]
#
# --- COMMIT END ---
# type: feat, fix, docs, style, refactor, test, chore, perf, ci, revert
# scope: 変更範囲(ui, api, auth, db, config, deps, ci など)
# description: 変更内容の簡潔な説明(50文字以内、日本語OK)
# body: 変更理由や詳細説明(オプション)
# footer: Issue番号や破壊的変更の説明(オプション)
#
# 例:
# feat(auth): ユーザー認証機能を追加
#
# Google OAuth 2.0による認証機能を実装
# - ログイン・ログアウト機能
# - セッション管理
# - 認証状態の永続化
#
# Closes #123
EOF
# テンプレートをGitに設定
git config commit.template .gitmessage
インタラクティブなコミットヘルパー
javascript// scripts/commit-helper.js
const inquirer = require('inquirer');
const { execSync } = require('child_process');
const types = [
{ name: '✨ feat: 新機能', value: 'feat' },
{ name: '🐛 fix: バグ修正', value: 'fix' },
{ name: '📚 docs: ドキュメント', value: 'docs' },
{ name: '💄 style: フォーマット', value: 'style' },
{
name: '♻️ refactor: リファクタリング',
value: 'refactor',
},
{ name: '✅ test: テスト', value: 'test' },
{ name: '🔧 chore: ビルド・設定', value: 'chore' },
{ name: '⚡ perf: パフォーマンス', value: 'perf' },
{ name: '👷 ci: CI設定', value: 'ci' },
{ name: '⏪ revert: 取り消し', value: 'revert' },
];
const scopes = [
'ui',
'api',
'auth',
'db',
'config',
'deps',
'ci',
];
async function createCommitMessage() {
const answers = await inquirer.prompt([
{
type: 'list',
name: 'type',
message: 'コミットタイプを選択:',
choices: types,
},
{
type: 'list',
name: 'scope',
message: 'スコープを選択(オプション):',
choices: ['', ...scopes],
default: '',
},
{
type: 'input',
name: 'description',
message: '変更内容を入力:',
validate: (input) =>
input.length > 0 && input.length <= 50,
},
{
type: 'input',
name: 'body',
message: '詳細説明(オプション):',
},
{
type: 'input',
name: 'footer',
message: 'Issue番号など(オプション):',
},
]);
const { type, scope, description, body, footer } =
answers;
let message = `${type}`;
if (scope) message += `(${scope})`;
message += `: ${description}`;
if (body) {
message += `\n\n${body}`;
}
if (footer) {
message += `\n\n${footer}`;
}
console.log('\n生成されたコミットメッセージ:');
console.log('---');
console.log(message);
console.log('---');
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'このメッセージでコミットしますか?',
},
]);
if (confirm) {
execSync(`git commit -m "${message}"`, {
stdio: 'inherit',
});
}
}
createCommitMessage().catch(console.error);
package.json にヘルパースクリプトを追加
json{
"scripts": {
"commit": "node scripts/commit-helper.js",
"commit:quick": "git add . && yarn commit"
},
"devDependencies": {
"inquirer": "^9.2.0"
}
}
プッシュ前の最終チェック
pre-push フックを活用して、リモートリポジトリにプッシュする前の最終チェックを行いましょう。
pre-push フックの活用
pre-push フックは、git push
実行前に自動実行されるスクリプトです。より包括的なチェックを実施できます。
基本的な pre-push フックの設定
bash# pre-pushフックを追加
yarn husky add .husky/pre-push "yarn test && yarn build"
.husky/pre-push ファイルの詳細設定
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 色付きメッセージの設定
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo "${BLUE}🚀 プッシュ前の最終チェックを開始...${NC}"
# 1. ブランチ名の検証
current_branch=$(git branch --show-current)
if [[ "$current_branch" =~ ^(master|main|develop)$ ]]; then
echo "${YELLOW}⚠️ 保護されたブランチへの直接プッシュです${NC}"
read -p "続行しますか? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "${RED}❌ プッシュがキャンセルされました${NC}"
exit 1
fi
fi
# 2. リモートとの差分チェック
echo "${YELLOW}📡 リモートとの差分をチェック中...${NC}"
git fetch origin $current_branch 2>/dev/null
if git diff HEAD origin/$current_branch --quiet; then
echo "${GREEN}✅ リモートとの差分はありません${NC}"
else
echo "${YELLOW}📊 リモートとの差分が検出されました${NC}"
fi
# 3. TypeScript型チェック
echo "${YELLOW}📝 TypeScript型チェック中...${NC}"
if ! yarn type-check; then
echo "${RED}❌ TypeScriptエラーが検出されました${NC}"
exit 1
fi
# 4. テスト実行
echo "${YELLOW}🧪 テストを実行中...${NC}"
if ! yarn test --passWithNoTests --coverage; then
echo "${RED}❌ テストが失敗しました${NC}"
exit 1
fi
# 5. ビルドチェック
echo "${YELLOW}🔨 ビルドチェック中...${NC}"
if ! yarn build; then
echo "${RED}❌ ビルドが失敗しました${NC}"
exit 1
fi
echo "${GREEN}✅ すべてのチェックが完了しました。プッシュを続行します${NC}"
TypeScript 型チェックの統合
TypeScript プロジェクトでは、実行時エラーを防ぐため型チェックを必須にしましょう。
効率的な型チェック設定
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "📝 TypeScript型チェックを実行中..."
# インクリメンタル型チェック(高速)
if yarn type-check --incremental --tsBuildInfoFile .tsbuildinfo; then
echo "✅ 型チェック完了"
else
echo "❌ 型エラーが検出されました"
echo ""
echo "修正方法:"
echo " yarn type-check # エラー詳細を確認"
echo " yarn type-check --watch # ウォッチモードで修正"
exit 1
fi
package.json の型チェックスクリプト
json{
"scripts": {
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch",
"type-check:incremental": "tsc --noEmit --incremental",
"type-check:strict": "tsc --noEmit --strict"
}
}
テスト実行の自動化
品質保証のため、プッシュ前にテストの実行を自動化しましょう。
効率的なテスト実行戦略
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🧪 テスト実行戦略を決定中..."
# 変更されたファイルを取得
changed_files=$(git diff --cached --name-only)
test_files=$(echo "$changed_files" | grep -E '\.(test|spec)\.(ts|tsx|js|jsx)$' || true)
if [ -n "$test_files" ]; then
echo "🎯 変更されたテストファイルを実行"
yarn test $test_files --passWithNoTests
elif [ -n "$changed_files" ]; then
echo "🔍 関連テストを実行"
yarn test --findRelatedTests $changed_files --passWithNoTests
else
echo "📋 全テストを実行"
yarn test --passWithNoTests
fi
テスト設定の最適化
json{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:related": "jest --findRelatedTests",
"test:changed": "jest --onlyChanged",
"test:ci": "jest --ci --coverage --watchAll=false"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts",
"!src/**/*.test.{ts,tsx}"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
チーム開発での運用設定
大規模なチーム開発では、個人の設定だけでなく、チーム全体での統一された運用が重要です。
共有設定の管理方法
チーム全体で一貫した設定を維持するための方法を解説します。
設定ファイルの一元管理
perlproject-root/
├── .husky/ # Git hooks設定
│ ├── pre-commit
│ ├── commit-msg
│ └── pre-push
├── .eslintrc.js # ESLint設定
├── commitlint.config.js # commitlint設定
├── prettier.config.js # Prettier設定
├── package.json # 依存関係とスクリプト
└── scripts/ # 共有スクリプト
├── setup-husky.sh
└── validate-environment.sh
環境構築自動化スクリプト
bash#!/bin/bash
# scripts/setup-husky.sh
echo "🛠️ 開発環境をセットアップ中..."
# Node.jsバージョンチェック
node_version=$(node -v | cut -d'v' -f2)
required_version="18.0.0"
if [ "$(printf '%s\n' "$required_version" "$node_version" | sort -V | head -n1)" != "$required_version" ]; then
echo "❌ Node.js $required_version 以上が必要です(現在: $node_version)"
exit 1
fi
# Yarnの確認
if ! command -v yarn &> /dev/null; then
echo "❌ Yarnがインストールされていません"
echo "インストール方法: npm install -g yarn"
exit 1
fi
# 依存関係のインストール
echo "📦 依存関係をインストール中..."
yarn install
# Huskyの初期化
echo "🐕 Huskyを初期化中..."
yarn husky install
# Git設定の確認
echo "🔧 Git設定を確認中..."
if [ -z "$(git config user.name)" ] || [ -z "$(git config user.email)" ]; then
echo "⚠️ Git設定が不完全です"
echo "設定方法:"
echo " git config user.name 'Your Name'"
echo " git config user.email 'your.email@example.com'"
fi
# コミットテンプレートの設定
if [ -f ".gitmessage" ]; then
git config commit.template .gitmessage
echo "✅ コミットテンプレートを設定しました"
fi
echo "🎉 セットアップ完了!"
echo ""
echo "次のステップ:"
echo " yarn lint # コード品質をチェック"
echo " yarn test # テストを実行"
echo " yarn commit # インタラクティブコミット"
package.json でのセットアップ統合
json{
"scripts": {
"postinstall": "husky install",
"setup": "chmod +x scripts/setup-husky.sh && ./scripts/setup-husky.sh",
"validate": "yarn lint && yarn type-check && yarn test"
}
}
CI との連携パターン
Husky との重複を避けつつ、CI で包括的なチェックを行う設定を構築しましょう。
GitHub Actions 設定例
yaml# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
with:
# Huskyとの重複チェックを避けるため、履歴を取得
fetch-depth: 0
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Validate commits (only on PR)
if: github.event_name == 'pull_request'
run: |
yarn commitlint --from ${{ github.event.pull_request.base.sha }} --to HEAD
- name: Run ESLint
run: yarn lint
- name: Run TypeScript check
run: yarn type-check
- name: Run tests
run: yarn test:ci
- name: Build application
run: yarn build
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
重複チェックの回避設定
bash# .husky/pre-push(CI連携版)
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# CI環境では実行をスキップ
if [ "$CI" = "true" ]; then
echo "🤖 CI環境のため pre-push チェックをスキップ"
exit 0
fi
# ローカル環境でのみ実行
echo "🔍 ローカル環境でのプッシュ前チェック"
# 軽量なチェックのみ実行(詳細はCIで)
yarn lint-staged && yarn type-check --incremental
トラブルシューティング
よくある問題とその解決方法をまとめました。
よくある問題と解決法
# | 問題 | 原因 | 解決方法 |
---|---|---|---|
1 | Husky が動作しない | 権限不足 | chmod +x .husky/* |
2 | lint-staged が遅い | 大量ファイル処理 | 並列実行とファイル除外 |
3 | CI との重複実行 | 設定重複 | 環境変数での分岐 |
4 | コミットできない | 厳しすぎるルール | 段階的なルール適用 |
デバッグとログ出力
bash#!/usr/bin/env sh
# .husky/pre-commit(デバッグ版)
. "$(dirname -- "$0")/_/husky.sh"
# デバッグモードの有効化
if [ "$HUSKY_DEBUG" = "1" ]; then
set -x # コマンドトレースを有効化
fi
# ログファイルの設定
LOG_FILE=".husky/logs/pre-commit-$(date +%Y%m%d-%H%M%S).log"
mkdir -p .husky/logs
# ログ出力関数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "🔍 pre-commit チェック開始"
# 環境情報のログ出力
log "Node.js: $(node -v)"
log "Yarn: $(yarn -v)"
log "Git: $(git --version)"
log "ブランチ: $(git branch --show-current)"
log "変更ファイル数: $(git diff --cached --name-only | wc -l)"
# 実際のチェック実行
if yarn lint-staged; then
log "✅ lint-staged 成功"
else
log "❌ lint-staged 失敗"
exit 1
fi
log "🎉 pre-commit チェック完了"
緊急時のバイパス方法
bash# 緊急時のコミット(品質チェックをスキップ)
git commit --no-verify -m "hotfix: 緊急修正"
# 緊急時のプッシュ(プッシュ前チェックをスキップ)
git push --no-verify
# 一時的なHusky無効化
HUSKY=0 git commit -m "一時的にHuskyを無効化"
パフォーマンス最適化
大規模プロジェクトで Husky と ESLint を効率的に動作させるための最適化手法を解説します。
大規模プロジェクトでの高速化
ファイル数が多い場合のパフォーマンス改善策を見ていきましょう。
ファイル除外の最適化
json{
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix --max-warnings 0"
],
// 大きなファイルを除外
"!(**/*.min.js|**/dist/**|**/build/**|**/node_modules/**)"
}
}
.eslintignore の活用
csharp# .eslintignore
# ビルド成果物
dist/
build/
out/
# 依存関係
node_modules/
.yarn/
# 一時ファイル
*.min.js
*.bundle.js
coverage/
# 自動生成ファイル
**/*.generated.*
**/generated/**
# 巨大なファイル
**/*.large.js
段階的チェックの実装
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 変更ファイル数による動的処理
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx|js|jsx)$' | wc -l)
echo "📊 ステージングファイル数: $STAGED_FILES"
if [ "$STAGED_FILES" -eq 0 ]; then
echo "📭 チェック対象ファイルがありません"
exit 0
elif [ "$STAGED_FILES" -le 5 ]; then
echo "🎯 小規模変更: 詳細チェック"
yarn lint-staged && yarn type-check
elif [ "$STAGED_FILES" -le 20 ]; then
echo "⚖️ 中規模変更: 標準チェック"
yarn lint-staged
else
echo "⚡ 大規模変更: 高速チェック"
yarn lint-staged --concurrent false
fi
インクリメンタルチェック
変更されたファイルのみを効率的にチェックする仕組みを構築しましょう。
TypeScript インクリメンタルチェック
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# インクリメンタル型チェック用の設定
TSC_OPTIONS="--noEmit --incremental --tsBuildInfoFile .tsbuildinfo"
if [ -f ".tsbuildinfo" ]; then
echo "🔄 インクリメンタル型チェック実行中..."
yarn tsc $TSC_OPTIONS
else
echo "🆕 初回型チェック実行中..."
yarn tsc $TSC_OPTIONS
fi
ESLint キャッシュの活用
json{
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx --cache --cache-location .eslintcache",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix --cache --cache-location .eslintcache"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix --cache --cache-location .eslintcache"
]
}
}
並列実行の設定
複数のチェックを並列実行してパフォーマンスを向上させましょう。
並列実行スクリプト
bash#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🚀 並列チェック開始..."
# 背景で実行するプロセス
yarn lint-staged &
LINT_PID=$!
yarn type-check --incremental &
TYPE_PID=$!
# プロセスの完了を待機
wait $LINT_PID
LINT_EXIT=$?
wait $TYPE_PID
TYPE_EXIT=$?
# 結果の確認
if [ $LINT_EXIT -eq 0 ] && [ $TYPE_EXIT -eq 0 ]; then
echo "✅ すべてのチェックが完了"
exit 0
else
echo "❌ チェックが失敗しました"
exit 1
fi
package.json での並列実行
json{
"scripts": {
"check:parallel": "run-p lint:check type-check test:quick",
"lint:check": "eslint . --ext .ts,.tsx,.js,.jsx --cache",
"type-check": "tsc --noEmit --incremental",
"test:quick": "jest --onlyChanged --passWithNoTests"
},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
高度な並列制御
javascript// scripts/parallel-checks.js
const { spawn } = require('child_process');
const os = require('os');
const checks = [
{
name: 'ESLint',
command: 'yarn',
args: ['lint-staged'],
},
{
name: 'TypeScript',
command: 'yarn',
args: ['type-check', '--incremental'],
},
{
name: 'Tests',
command: 'yarn',
args: ['test', '--onlyChanged', '--passWithNoTests'],
},
];
// CPU数に基づく並列数の制御
const maxParallel = Math.min(
checks.length,
os.cpus().length
);
async function runChecks() {
console.log(`🔄 ${maxParallel} 並列でチェック実行中...`);
const results = await Promise.allSettled(
checks
.slice(0, maxParallel)
.map((check) => runCommand(check))
);
const failed = results.filter(
(result) => result.status === 'rejected'
);
if (failed.length > 0) {
console.error('❌ チェックが失敗しました');
process.exit(1);
} else {
console.log('✅ すべてのチェックが完了');
}
}
function runCommand({ name, command, args }) {
return new Promise((resolve, reject) => {
console.log(`🔍 ${name} チェック開始...`);
const child = spawn(command, args, {
stdio: 'inherit',
});
child.on('close', (code) => {
if (code === 0) {
console.log(`✅ ${name} 完了`);
resolve();
} else {
console.error(
`❌ ${name} 失敗 (exit code: ${code})`
);
reject(new Error(`${name} failed`));
}
});
});
}
runChecks().catch(console.error);
まとめ
Husky と ESLint を組み合わせたコミット前チェック自動化により、チーム開発での品質保証を大幅に改善できます。本記事では、基本的なセットアップから高度な運用まで、実践的な設定方法を詳しく解説いたしました。
導入効果の整理
- 品質向上: 問題のあるコードのリポジトリ混入を防止
- 効率化: 自動チェックによりレビュー負荷を軽減
- 統一性: チーム全体で一貫したコーディング規約を維持
- 早期発見: 開発段階での問題発見により修正コストを削減
成功のポイント
- 段階的導入: 基本設定から始めて徐々に厳格化
- パフォーマンス重視: 大規模プロジェクトでも快適な開発体験を維持
- チーム合意: ルールの意図を共有し、適切なエスケープハッチを用意
- 継続的改善: 開発者のフィードバックを基にした設定の最適化
今後の展望 AI 支援ツールとの連携や、より高度な静的解析ツールとの統合により、さらなる品質向上が期待できます。重要なのは、技術的な完璧さよりも、チーム全体が継続して使える実用的なシステムを構築することです。
適切に設定された Husky と ESLint の組み合わせは、開発チームの生産性向上と品質保証の両立を実現する強力なツールとなるでしょう。
関連リンク
- review
チーム開発が劇的に変わった!『リーダブルコード』Dustin Boswell & Trevor Foucher
- review
アジャイル初心者でも大丈夫!『アジャイルサムライ − 達人開発者への道』Jonathan Rasmusson
- review
人生が作品になる!『自分の中に毒を持て』岡本太郎
- review
体調不良の 99%が解決!『眠れなくなるほど面白い 図解 自律神経の話』小林弘幸著で学ぶ、現代人必須の自律神経コントロール術と人生を変える健康革命
- review
衝撃の事実!『睡眠こそ最強の解決策である』マシュー・ウォーカー著が明かす、99%の人が知らない睡眠の驚くべき真実と人生を変える科学的メカニズム
- review
人生が激変!『嫌われる勇気』岸見一郎・古賀史健著から学ぶ、アドラー心理学で手に入れる真の幸福と自己実現