Многим пользователям git, я думаю, знакома ситуация, когда после коммита обнаруживается, что в коммит попало что-то лишнее, или закоммиченый код непроходящий тесты. А иногда этот код, ко всему прочему, отправляется напрямую в origin.

Чего только стоит галочка ‘Push changes immediately to origin/branch_name’ в Source Tree: Кнопка Push changes immediately to origin/branch_name в SourceTree

Думаю, почти у каждого возникло логичное решение использовать git hooks. Но так ли это удобно?

Чтобы локально настроить git hooks, необходимо перейти в скрытую директорию .git/hooks, найти необходимый хук и вставить команды для проверки кода. Казалось бы все просто. Но все это пока хуки необходимы только вам. А если хочется, чтобы hook был настроен уже после разворачивания проекта автоматически? И новый разработчик на проекте не напрягал CI почем зря только из-за того, что он не заметил написанный console.log.

К счастью, решение - это очень удобный npm пакет husky. Установить его можно как и любой другой пакет командой

npm install husky --save-dev

После установки осталось только сконфигурировать наши хуки. Для этого достаточно просто дописать новую секцию husky в свой package.json и указать необходимые команды на хуки:

{
    "name": "demo",
    "dependencies": {},
    "devDependencies": {
        "husky": "^1.1.2"
    },
    "husky": {
        "hooks": {
            "pre-commit": "gulp lint",
            "pre-push": "gulp test:unit",
            "...": "..."
        }
    }
}

После этого при попытке седлать коммит, сначала отработает команда lint. Больше не нужно думать о пути к node, выполнении скриптов из папки bin ./node_modules/.bin/gulp test и прочего всего. После установки всех зависимостей хук автоматически будет применен.

Если же Вы захотите изменить какую либо команду, достаточно просто внести соответствующие изменения в package.json и закоммитить изменения после чего хук изменится для всех разработчиков.

Также библиотека имеет возможность конфигурации через отдельный файл .huskyrc, .huskyrc.json или .huskyrc.js. Но на мой взгляд, для столь незначительного количества конфигурации выносить это в отдельный файл в корне проекта бессмысленно.

Как это работает?

Для всех модулей npm существует возможность выполнения каких-либо скриптов после определенных событий. Например, после установки пакета (подробнее можно прочитать в документации). husky на событие postinstall, которое всегда выполнятся после установки пакета, регистрирует свой скрипт. Который находит в родительских директориях папку .git и прописывает основной модуль пакета на выполнение во все доступные хуки. В последствии когда происходит вызов хука, модуль husky ищет все возможные описания команд хуков в package.json, .huskyrc или других файлах и выполняет их. Вот и вся магия.