Настройка и использование Pre-commit хуков в проекте
Этот документ описывает, как настроены и работают pre-commit хуки в проекте, которые автоматически проверяют код перед коммитом.
Обзор
Pre-commit хуки автоматически запускаются перед каждым коммитом и проверяют измененные файлы на соответствие стандартам кода. Это помогает поддерживать качество кода и предотвращает попадание проблемного кода в репозиторий.
В проекте используются следующие инструменты:
- Husky 9 — для управления Git хуками
- lint-staged 16 — для запуска линтеров только на staged файлах
- ESLint — для проверки качества кода
- Prettier — для форматирования кода
Архитектура решения
Принцип работы
- При попытке сделать коммит Git автоматически запускает pre-commit хук
- Хук проверяет, есть ли изменения во фронтенде (
src/main/webapp/app/react/) - Если изменений во фронтенде нет — хук пропускается
- Если есть изменения — запускается
lint-staged, который:- Находит все staged файлы, соответствующие паттернам
- Запускает ESLint с отдельным конфигом для pre-commit
- Запускает Prettier для форматирования
- Блокирует коммит при наличии ошибок
Структура файлов
meta/
├── .husky/
│ └── pre-commit # Git хук, запускаемый перед коммитом
├── eslint.config.mjs # Основной конфиг ESLint (warn для большинства правил)
├── eslint.pre-commit.config.mjs # Строгий конфиг для pre-commit (error)
└── package.json # Конфигурация lint-stagedHusky
Установка и настройка
Husky устанавливается как dev-зависимость и автоматически инициализируется через скрипт prepare:
{
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"husky": "^9.1.7"
}
}После установки зависимостей (npm install) Husky автоматически настраивает Git хуки.
Pre-commit хук
Файл .husky/pre-commit содержит логику проверки изменений:
# Проверяем, есть ли изменения во фронтенде
FRONTEND_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '^src/main/webapp/app/react/' || true)
if [ -z "$FRONTEND_FILES" ]; then
echo "No frontend files changed, skipping pre-commit hooks"
exit 0
fi
echo "Frontend files changed, running lint-staged..."
npx lint-stagedКак это работает:
git diff --cached --name-only— получает список staged файлов--diff-filter=ACM— фильтрует только добавленные (A), измененные (C) и модифицированные (M) файлыgrep -E '^src/main/webapp/app/react/'— проверяет, есть ли файлы во фронтенде- Если файлов нет — хук завершается успешно
- Если есть — запускается
lint-staged
Примеры:
# Коммит только backend файлов — хук пропускается
git add src/main/java/MyClass.java
git commit -m "Update backend" # Хук не запускается
# Коммит фронтенд файлов — хук запускается
git add src/main/webapp/app/react/src/components/MyComponent.tsx
git commit -m "Update component" # Хук проверяет файлlint-staged
Конфигурация
Конфигурация lint-staged находится в package.json:
{
"lint-staged": {
"src/main/webapp/app/react/**/*.{ts,tsx}": [
"eslint --config eslint.pre-commit.config.mjs --fix",
"prettier --write"
],
"src/main/webapp/app/react/**/*.{js,jsx,json,css,less,scss,md}": [
"prettier --write"
]
}
}Паттерны файлов
-
**/*.{ts,tsx}— TypeScript и TSX файлы- Запускается ESLint с автоматическим исправлением
- Запускается Prettier для форматирования
-
**/*.{js,jsx,json,css,less,scss,md}— остальные файлы- Запускается только Prettier для форматирования
Как работает lint-staged
- Находит все staged файлы, соответствующие паттернам
- Запускает команды для каждого файла
- Автоматически добавляет исправленные файлы обратно в staging
- Если команда возвращает ошибку — блокирует коммит
Пример:
# Исходный файл с ошибками
const test: any = 0; # Использование any
# После git add и попытки коммита
git add src/main/webapp/app/react/src/test/test.ts
git commit -m "Add test"
# lint-staged запускает:
# 1. eslint --fix (находит ошибку с any)
# 2. prettier --write (форматирует код)
# Коммит блокируется из-за ошибки ESLintESLint конфигурация для pre-commit
Отдельный конфиг
Для pre-commit используется отдельный конфиг eslint.pre-commit.config.mjs, который более строгий, чем основной eslint.config.mjs.
Причина разделения:
- Основной конфиг (
eslint.config.mjs) используетwarnдля большинства правил, чтобы не блокировать сборку проекта - Pre-commit конфиг (
eslint.pre-commit.config.mjs) используетerrorдля критичных правил, чтобы блокировать коммиты с проблемным кодом
Критичные правила (error)
Эти правила блокируют коммит при нарушении:
TypeScript
-
@typescript-eslint/no-explicit-any: 'error'
Блокирует использование типаany.// Ошибка — коммит заблокирован const data: any = {}; // Правильно const data: Record<string, unknown> = {}; -
@typescript-eslint/no-unused-vars: 'error'
Блокирует неиспользуемые переменные.// Ошибка — коммит заблокирован const unusedVar = 42; // Правильно const usedVar = 42; console.log(usedVar);
React Hooks
-
react-hooks/rules-of-hooks: 'error'
Блокирует нарушение правил хуков.// Ошибка — хук вызывается в условии if (condition) { useEffect(() => { // ... }); } // Правильно useEffect(() => { if (condition) { // ... } }); -
react-hooks/exhaustive-deps: 'error'
Блокирует отсутствие зависимостей в хуках.// Ошибка — отсутствует зависимость useEffect(() => { console.log(value); }, []); // value не указан в зависимостях // Правильно useEffect(() => { console.log(value); }, [value]);
Общие правила
-
no-var: 'error'
Блокирует использованиеvar.// Ошибка var count = 0; // Правильно let count = 0; // или const count = 0; -
@typescript-eslint/ban-ts-comment: 'error'
Блокирует использование@ts-ignoreи подобных комментариев без объяснения.// Ошибка // @ts-ignore const result = someFunction(); // Правильно (с объяснением) // @ts-expect-error - функция возвращает any, но мы знаем тип const result: MyType = someFunction();
Полный список правил
Все правила в eslint.pre-commit.config.mjs настроены как error, что означает, что любое нарушение блокирует коммит:
rules: {
// Критичные правила для pre-commit - блокируем коммит при ошибках
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
// Остальные правила также настроены как error
"@typescript-eslint/no-this-alias": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/no-unused-expressions": "error",
"@typescript-eslint/no-empty-object-type": "error",
"react/no-deprecated": "error",
"react/prop-types": "error",
"react/no-unknown-property": "error",
"no-var": "error",
"import/no-unresolved": "error",
"import/named": "error",
"prefer-spread": "error",
"prefer-rest-params": "error"
}Prettier
Prettier автоматически форматирует код согласно настройкам проекта (.prettierrc):
{
"printWidth": 120,
"trailingComma": "all",
"singleQuote": true,
"semi": true,
"arrowParens": "avoid"
}Как работает:
- Prettier автоматически форматирует staged файлы
- Отформатированные файлы автоматически добавляются обратно в staging
- Если форматирование изменило файл, коммит продолжается с отформатированным кодом
Пример:
// До форматирования
const component = (props:{name:string,age:number})=>{return<div>{props.name}</div>}
// После Prettier (автоматически)
const component = (props: { name: string; age: number }) => {
return <div>{props.name}</div>;
};Рабочий процесс
Типичный сценарий
-
Разработчик вносит изменения:
# Редактирует файл vim src/main/webapp/app/react/src/components/MyComponent.tsx -
Добавляет файлы в staging:
git add src/main/webapp/app/react/src/components/MyComponent.tsx -
Пытается сделать коммит:
git commit -m "Update component" -
Pre-commit хук запускается:
Frontend files changed, running lint-staged... -
lint-staged проверяет файл:
- ESLint находит ошибки (если есть)
- Prettier форматирует код
- Если есть ошибки ESLint — коммит блокируется
- Если ошибок нет — коммит проходит
Обработка ошибок
Если ESLint находит ошибки:
$ git commit -m "Update component"
Frontend files changed, running lint-staged...
✖ eslint --config eslint.pre-commit.config.mjs --fix:
src/main/webapp/app/react/src/components/MyComponent.tsx
1:13 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
✖ 1 problem (1 error, 0 warnings)
husky - pre-commit script failed (code 1)Что делать:
- Исправить ошибки в коде
- Добавить исправленные файлы снова:
git add ... - Попробовать коммит снова
Если нужно временно пропустить проверку (не рекомендуется):
git commit --no-verify -m "Update component"Важно: Используйте --no-verify только в исключительных случаях. Лучше исправить ошибки перед коммитом.
Преимущества
Автоматическая проверка
- Код проверяется автоматически перед каждым коммитом
- Не нужно помнить о запуске линтеров вручную
- Проблемы обнаруживаются до попадания в репозиторий
Только измененные файлы
- Проверяются только файлы, которые были изменены
- Быстрая работа даже на больших проектах
- Не тратится время на проверку всего проекта
Разделение конфигов
- Основной конфиг ESLint не блокирует сборку
- Pre-commit конфиг строже и блокирует проблемный код
- Можно постепенно улучшать качество кода
Автоматическое форматирование
- Prettier автоматически форматирует код
- Единый стиль кода в проекте
- Меньше споров о форматировании в code review
Игнорируемые пути
Те же пути, что и в основном конфиге ESLint, игнорируются при проверке:
ignores: [
'docs/',
'node_modules/',
'out/',
'src/test/',
'src/main/webapp/app/_b/',
'src/main/webapp/app/vendor/',
'src/main/webapp/app/bower_components/',
'src/main/webapp/app/contrib/',
'src/main/webapp/app/react/node_modules/',
'src/main/webapp/app/react/src/components/external/'
]