Husky: Форматирование, линтинг и тестирование при коммите или пуше
Можно использовать несколько инструментов командной строки для обеспечения качества кодовой базы ещё до слияния изменений в репозиторий:
- Форматеры автоматически корректируют стиль кодирования, обеспечивая согласованность всей кодовой базы.
- Линтеры анализируют код, чтобы обнаружить и исправить проблемы, внедрить лучшие практики и предотвратить распространённые ошибки. (formatters vs. linters)
- Тестирующие фреймворки проверяют функциональность приложения на соответствие ожидаемым результатам.
Эти инструменты фантастичны, но есть одна загвоздка: вы и ваши товарищи должны не забывать запускать их перед отправкой кода в репозиторий. И, будучи людьми, иногда можно забыть это сделать.
Поэтому сегодня мы рассмотрим Husky, инструмент, автоматически выполняющий любое количество команд при коммите или пуше. Теперь не нужно беспокоиться, что вы забыли отформатировать, проверить или протестировать код перед загрузкой в репозиторий — Husky делает это каждый раз, при запуске git commit
или git push
. Давайте приступим.
Подготовка сцены
Предположим, мы работаем над проектом Laravel, содержащим код на PHP и JavaScript. Не волнуйтесь, эти инструкции будут работать, даже если вы не работаете именно с этим технологическим стеком.
Для PHP используем:
- Duster от Tighten, набор инструментов форматирования и линтинга для приложений Laravel:
./vendor/bin/duster fix
- Pest в качестве фреймворка для тестирования:
php artisan test
И для JavaScript:
- Prettier в качестве форматера:
npm run format
- ESLint в качестве линтера:
npm run lint
- Vitest в качестве фреймворка для тестирования:
npm run test
Husky в действии
Husky — это современное решение для управления Git хуками, пользовательскими скриптами, которые можно задать для запуска при наступлении определённых событий в Git. Созданный typicode (разработчиком json-server и jsonplaceholder) и насчитывающий более 10 миллионов еженедельных загрузок на npm, этот инструмент позволяет автоматизировать выполнение команд при таких событиях Git, как коммиты или пуши.
Для начала установите Husky в качестве зависимости для разработки:
npm install --save-dev husky
После установки инициализируйте Husky, выполнив следующую команду:
npx husky init
Обратите внимание, что для успешного выполнения этой команды потребуется сначала инициализировать Git-репозиторий.
Эта простая команда выполняет две важные задачи:
- Добавляет сценарий
prepare
в файлpackage.json
. - Создаёт каталог
.husky
в корневом каталоге проекта, содержащий файлpre-commit
.
Файл pre-commit
— это bash-скрипт, выполняющийся перед каждым коммитом; по умолчанию он содержит всего одну строку:
npm test
Можно изменить этот файл, чтобы включить в него дополнительные команды. Давайте адаптируем его для запуска команд, необходимых для данного проекта. Измените файл pre-commit
следующим образом:
npm run format
npm run lint
npm run test
./vendor/bin/duster fix
php artisan test
Теперь при выполнении коммита с использованием:
git add -A && git commit -m "My commit message"
Husky автоматически выполнит указанные команды, прежде чем разрешить выполнение коммита.
- Коммит будет выполнен успешно, если команды выполнятся без ошибок.
- Однако если во время выполнения какой-либо из команд возникнет ошибка, процесс коммита остановится, а вывод об ошибке будет выведен в терминал, чтобы можно было разобраться с ней, а затем попробовать выполнить коммит снова.
Представим, например, что есть неопределённый метод в файле JavaScript. Husky выполнит первую команду, npm run format
, без проблем. Затем перейдёт ко второй команде, npm run lint
, где столкнётся с ошибкой:
/dev/laravel-app/resources/js/bootstrap.js
4:1 error 'translate' is not defined no-undef
✖ 1 problem (1 error, 0 warnings)
husky - pre-commit script failed (code 1)
Husky приостановит выполнение следующих команд и отменит коммит, что позволит исправить проблему перед повторной попыткой коммита. Нет ничего лучше, чем поймать ошибку до того, как она вылетит!
После завершения настройки Husky добавьте папку .husky
в репозиторий, чтобы созданный нами хук pre-commit
выполнялся до того, как кто-то сделает коммит в кодовую базу.
Можно создать ещё один хук, создав несколько файлов в каталоге .husky
. Имя файла должно быть правильным именем Git-хука; например, pre-push
.
Как обрабатывать только staged файлы
Эта стратегия прекрасна, но вы могли заметить и недостаток: запуск форматера и линтера по всей кодовой базе может показаться чрезмерным при изменении всего нескольких файлов. Давайте разберёмся с этим.
В PHP
В Duster можно использовать флаг --dirty
, указывающий инструменту запускать линтеры или фиксеры только для staged файлов.
В нашем примере команда будет выглядеть так:
./vendor/bin/duster fix --dirty
Можно приступать к редактированию файла .husky/pre-commit
:
npm run format
npm run lint
npm run test
-./vendor/bin/duster fix
+./vendor/bin/duster fix --dirty
php artisan test
И мы закончили! Теперь перейдём к JavaScript.
В JavaScript
lint-staged — это инструмент, позволяющий выборочно выполнять проверки только тех файлов, которые мы отредактировали и передали для коммита, что значительно ускоряет выполнение проверки.
Чтобы использовать его, давайте установим его в качестве зависимости для разработки:
npm install --save-dev lint-staged
Добавьте этот скрипт в файл package.json
:
{
"scripts": {
// другие скрипты
"lint-staged": "lint-staged"
}
}
Теперь обновим хук pre-commit
в Husky. Откройте .husky/pre-commit
и замените несколько команд npm на ту, что только что добавили:
-npm run format
-npm run lint
-npm run test
+npm run lint-staged
./vendor/bin/duster fix --dirty
php artisan test
Наконец, создадим файл конфигурации с именем .lintstagedrc.json
в корне проекта. В этом файле указывается, какие команды запускать на основе расширений файлов. Точный набор команд может меняться в зависимости от установки, но в данном примере он будет таким:
{
"*.css": [
"prettier --write"
],
"*.{js,vue}": [
"prettier --write",
"eslint --ignore-path .gitignore --fix",
"vitest related --run --environment=jsdom"
]
}
Мы поручаем lint-staged
выполнить следующие задания:
Когда передаются CSS-файлы, то:
- Запустить Prettier только для этих файлов.
Когда передаются файлы JavaScript, то:
- Запустить Prettier только для этих файлов.
- Запустить ESLint только для этих файлов.
- И наконец, запустить Vitest.
Так, если у нас есть только три файла, lint-staged
проверит только эти три, пропуская остальную часть кодовой базы. Таким образом, мы экономим время, концентрируясь только на том, что является новым или изменённым.
$ git add -A && git commit -am "Add a cool feature"
> demo-husky@0.0.0 lint-staged
> lint-staged
✔ Preparing lint-staged...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
[main b57de91] Test lint-staged
2 files changed, 4 insertions(+), 7 deletions(-)
Мы запускаем весь набор тестов каждый раз, когда изменяем код JavaScript, потому что изменения могут иметь неожиданные побочные эффекты для других частей приложения. Однако если были отредактированы только CSS-файлы, можно смело считать, что тестирование JavaScript можно пропустить.
А если мы не добавили JavaScript или CSS файлы, то ни одна из этих проверок не будет запущена. Например, если мы добавим app/Models/User.php
, Husky запустит Duster и Pest, но ни одну из команд, включённых в lint-staged (Prettier, ESLint и Vitest).
В завершение
Вот и всё! Мы рассказали о том, как форматировать, проверять и тестировать фронтенд и бэкенд код.
Такая настройка позволит команде соблюдать стандарты стиля и качества кода проекта и отлавливать ошибки до того, как они будут слиты в основные ветки репозитория.
Да, поначалу может быть неприятно, когда коммит останавливается из-за ошибки. Но отдача значительно превосходит этот первоначальный дискомфорт. И вот вам маленький секрет: если вы хотите обойти Хаски, добавьте --no-verify
в конце команды коммита. Но никому не говорите, что я вам рассказал!
Вы можете получить доступ к полной кодовой базе этой статьи на этом публичном репозитории GitHub.
Надеюсь, вы сможете применить один или два совета из этого руководства. Если вы хотите видеть больше подобных материалов, дайте нам знать. До следующего раза!