Git Hooks не так страшны, как их малюют
Бывает, что в командах тестирования грезят об автоматизации процессов, но почему-то не внедряют даже такие простые вещи как хуки: «Они очень усложняют процесс, и писать их тысячу часов». С этим я категорически не согласен, ведь реализовать хуки очень просто.
Меня зовут Денис Федоров, я тестировщик в команде 2ГИС ПРО, сервисе геоаналитики для бизнеса. Рассказываю, что такое гит-хуки, чем они могут быть полезны и как их можно реализовать у себя.
Что же такое хуки в Git
Git-хуки — скрипты, которые автоматически запускаются при наступлении определённых событий в жизненном цикле репозитория, таких как commit, push, merge, rebase и другие операции. С их помощью можно автоматизировать различные аспекты рабочего процесса и выполнять необходимые проверки и действия на каждом этапе разработки.
Пример, как работают хуки
Хуки интегрируются в процесс создания коммита, позволяя автоматизировать и контролировать различные этапы разработки. Они дают разработчикам выполнять определенные действия до и после коммита, что обеспечивает дополнительный контроль и повышает качество кода.
Разберём по этапам.
Действия пользователя — User Actions
Шаг 1: подготовка изменений (Stage Changes). На этом этапе разработчик добавляет изменения в индекс (staging area) с помощью команды git add. Это означает, что изменения готовы для коммита.
Шаг 2: коммит изменений (Commit Changes). Разработчик выполняет команду git commit, чтобы зафиксировать подготовленные изменения в истории проекта.
Шаг 3: ввод сообщения коммита (Enter Commit Message). После команды git commit требуется ввести сообщение коммита, которое описывает сделанные изменения.
Шаг 4: завершение процесса (All Done).Коммит завершен, и изменения зафиксированы в истории репозитория.
Хуки (Hooks):
Хук до коммита (Pre-Commit Notification). Хук pre-commit запускается сразу после того, как были подготовлены изменения, но до того, как изменения будут зафиксированы. Этот хук обычно используется для проверки кода (например, линтеры), запуска тестов, или других задач, которые должны быть выполнены перед коммитом.
Хук после коммита (Post-Commit Notification). Хук post-commit запускается после того, как коммит зафиксирован в истории репозитория. Он может использоваться для уведомлений, запуска CI-процессов или других задач, которые необходимо выполнить после того, как изменения зафиксированы.
Польза
Для нашей команды решающим фактором внедрения хуков стало улучшение качества кода. Нам было важно, чтобы линтер пробегал бы не в CI/CD, а до коммита — чтобы не нагружать систему и не тратить лишнее время, а хуки как раз позволяют добавлять линтер перед push. Но у них есть и другие преимущества. Вот самое важное ↓
Автоматизация процессов. Git hooks автоматизируют рутинные задачи, это экономит время и помогает избежать ошибок. Например, Pre-commit hooks проверяют код на стиль или ошибки перед каждым коммитом, а Post-commit hooks автоматически запускают тесты после каждого коммита.
Улучшение качества кода. С помощью хуков можно настроить автоматические проверки качества кода, такие как линтинг для проверки стиля кода, статический анализ для поиска потенциальных ошибок и запуск тестов для проверки функциональности перед отправкой изменений в репозиторий.
Безопасность. Git hooks обеспечивают безопасность, предотвращают некорректные или вредоносные изменения. Например, Pre-receive hooks проверяют коммиты на сервере перед их принятием, а Update hooks — обновления веток.
Поддержка согласованности и стандартов. С помощью Git hooks можно гарантировать, что все разработчики следуют установленным стандартам и процедурам. Так Commit-msg hooksпроверяют формат сообщений коммитов на соответствие заданному шаблону, а Prepare-commit-msg hooks автоматически добавляют информацию в сообщение коммита (например, номер задачи из системы управления проектами).
Улучшение рабочего процесса. Git hooks интегрируют различные инструменты и процессы в рабочий процесс. Например, Post-checkout hooks автоматически переключают конфигурации окружения в зависимости от ветки, а Post-merge hooks выполняют необходимые действия после слияния, такие как миграция базы данных.
Виды
Cуществуют два вида хуков:
серверный, в этом случае файл един для всех.
клиентский, такие хуки хранятся локально на компьютере разработчика (или любого другого члена команды) и выполняются только для конкретного разработчика, то есть они невидимы для остальных.
Ниже сравнил серверные и клиентские хуки по различным критериям↓
Критерий | Клиентские Git-хуки | Серверные Git-хуки |
Место выполнения | Локальная машина разработчика | Сервер, на котором расположен репозиторий |
Примеры хуков | pre-commit, pre-push, commit-msg, post-checkout | pre-receive, post-receive, post-update, pre-rebase |
Примеры использования | Проверка синтаксиса, форматирование кода, генерация документации | Запуск CI/CD-пайплайнов, проверка прав доступа, блокировка мержей с конфликтами |
Контроль | Индивидуальный контроль каждым разработчиком | Центральный контроль администратором репозитория |
Настройка | Настраиваются на каждом локальном рабочем месте | Настраиваются один раз на сервере и применяется ко всем |
Уровень безопасности | Менее безопасны, так как разработчики могут отключить или изменить хуки | Более безопасны, обязательны для всех |
Принуждение исполнения правил | Правила могут быть легко обойдены или отключены | Правила применяются ко всем пользователям и обязательны |
Время выполнения | Выполняются в реальном времени во время работы с Git | Выполняются при операциях с сервером, таких как push, merge |
Обратная связь | Мгновенная, может улучшить рабочий процесс разработчика | Задержка до операции с сервером, может замедлить push |
Производительность | Может замедлять работу на локальной машине при сложных проверках | Не влияет на локальную работу, но может замедлить серверные операции |
Совместимость | Зависит от среды разработки (операционная система, инструменты) | Универсально, не зависит от среды разработчика |
Простота установки | Каждый разработчик должен самостоятельно настроить хуки | Устанавливаются и настраиваются администратором репозитория |
Автоматизация | Может использоваться для автоматизации задач на стороне клиента (например, форматирование кода) | Применяются для автоматизации задач на сервере (например, запуск тестов) |
Объем проверок | Ограничен возможностями локальной машины | Может включать сложные проверки и использование серверных ресурсов |
Гибкость | Высокая гибкость, настройка под конкретные нужды разработчика | Меньшая гибкость, ориентир на общие правила и процессы в команде |
Зависимость от окружения | Может зависеть от установленных инструментов и библиотек | Обычно не зависит от окружения разработчика |
Использование ресурсов | Используют ресурсы локальной машины | Используют серверные ресурсы |
Поддержка командной работы | Менее подходят для применения в команде, так как у разных разработчиков могут быть отличия | Отлично поддерживают командную работу через единые правила |
Применимость к репозиториям | Применимы только на локальной копии репозитория | Применимы ко всему репозиторию на сервере |
Масштабируемость | Сложно масштабировать на большой команде | Хорошо масштабируются и применяются на всех участников команды |
Получается, что серверные хуки обеспечивают централизованный контроль и автоматизацию, что важно для больших команд с высокими требованиями к качеству и безопасности. Но их настройка и обслуживание могут быть сложными, и есть риск влияния на производительность. Клиентские хуки более гибки и удобны для небольших команд или одиночных разработчиков, но они менее надёжны для поддержания общих стандартов и безопасности в крупных проектах.
Учитывая, что наша команда достаточно большая, настройка линтера необходима каждому. Но поскольку настройка локального pre-commit хука для каждого отдельного участника команды занимает много времени, мы выбрали серверные хуки. Такое решение позволяет одному человеку писать и настраивать хук, после чего он доступен всей команде. Это значительно сокращает затраты времени и усилий на внедрение хуков.
Реализация
Ну что ж, перейдём к практической части и реализуем pre-commit хук, который запускает линтер для всех измененных файлов в коммите. В качестве линтера будем использовать pylint (но вообще можно установить совершенно любой).
Шаг первый
Создать или отредактировтаь pre-commit скрипт (он может лежать и называться как угодно).
Шаг второй
Написать скрипт, в котором учесть:
проверку наличия линтера установленного на локальной машине,
получение списка всех python-файлов, подготовленных для коммита,
условия выхода, если нет python-файлов,
и запуск линтера, если файлы есть.
Пример:
# Проверяем наличие pylint
if ! [ -x "$(command -v pylint)" ]; then
echo 'Error: pylint is not installed.' >&2
exit 1
fi
# Получение списка всех Python-файлов, подготовленных для коммита
files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
# Если нет Python-файлов, выход
if [ -z "$files" ]; then
exit 0
fi
# Запуск pylint для всех подготовленных Python-файлов
pylint $files
if [ $? -ne 0 ]; then
echo "Pylint found issues. Commit aborted."
exit 1
fi
Шаг третий
Установить плагин pre-commit и создать файл .pre-commit-config.yaml в коренной папке проекта. Данный плагин помогает запускать много разных скриптов из разных мест в pre-commit, благодаря чему не потребуется в одном файле писать 1000 строк разных скриптов.
В данном файлике надо прописать путь до нашего файла с pre-commit:
repos:
- repo: local # Указывается local, так как файлик хука у нас находится в репозитории
hooks:
- id: custom-hook # Уникальный идентификатор хука
name: Run my custom hook # Имя хука
entry: ./pre-commit.sh # Путь к хуку
language: script # указание, что скрипт выполняется как обычный скрипт (script).
Шаг четвертый
Установить хуки из конфига pre-commit
pre-commit install
Эта команда создаст символическую ссылку на наш скрипт pre-commit.sh в директории .git/hooks, чтобы он выполнялся перед каждым коммитом.
Готово!
Теперь перед каждым коммитом будет запускаться наш хук с линтером. Он предотвратит коммит файлов, не соответствующих требованиям линтера, что поможет избежать запуска неоптимальных пайплайнов и сэкономит время и ресурсы CI/CD системы.
Выводы
Хуки — это простой и очень полезный инструмент. Однако, несмотря на все их преимущества, некоторые специалисты всё ещё опасаются их использовать (мы сначала тоже!) Поэтому в конце разобрал основные страхи, связанные с хуками, и как мы сами с ними справлялись.
Скрытый текст
Замедление рабочего процесса
Миф: Хуки сильно замедляют процесс коммита и пуша, делая их неудобными.
Реальность: Хуки могут добавлять незначительную задержку, но их эффективность в автоматизации и обеспечении качества кода чаще всего перекрывает эту потерю времени. Например, наши хуки проверяют лишние импорты и линтер Code Style. В результате некорректный код не будет запушен, и CI/CD-пайплайн не провалится на проверке стилевого оформления. Это предотвращает ситуацию, когда нужно будет заново коммитить и запускать пайплайн только из-за падения линтера.
Сложность настройки и поддержки
Миф: Настройка хуков сложна и требует значительных усилий для поддержки.
Реальность: Большинство хуков легко настраиваются с помощью готовых решений и скриптов. Современные инструменты и документация значительно упрощают этот процесс. Например, в нашем случае всё свелось к включению соответствующей опции в GitLab и добавлению файла с тремя строчками для запуска линтера при пуше. Поддержка сводится лишь к редактированию пути до Make-команды в случае ее правок.
Ограничение гибкости разработчиков
Миф: Хуки ограничивают свободу разработчиков и усложняют индивидуальные рабочие процессы.
Реальность: Хуки можно адаптировать под разные рабочие процессы, например, настроить их так, чтобы они применялись только к определённым веткам или типам коммитов. В нашем случае линтер уже использовался всеми разработчиками и тестировщиками, поэтому добавление его в хуки прошло без проблем. Кроме того, можно чётко указать, к каким файлам применять хук, что позволяет сохранить гибкость.
Необходимость установки на каждой машине
Миф: Хуки нужно устанавливать на каждой машине разработчика, что вызывает трудности в их распространении.
Реальность: Существуют инструменты, такие как Husky для Node.js, которые автоматически управляют установкой хуков при клонировании репозитория. В первой реализации мы не добавили такой плагин, но в будущем планируем установить Husky и распространять хуки с её помощью. Для каждого языка есть свои инструменты, примеры: pre-commit для Python, lefthook для Go и overcommit для Ruby.
Потенциальные конфликты и ошибки
Миф: Хуки могут вызвать конфликты и ошибки, особенно если они не корректно написаны.
Реальность: При правильной настройке и тщательном тестировании, проблемы с хуками можно свести к минимуму. Хорошей практикой является наличие тестов и проверок для самих хуков.
Необходимость знания скриптового языка
Миф: Для написания хуков необходимо знать скриптовые языки (например, Bash или Python).
Реальность: Базовые хуки можно создать с минимальными знаниями скриптовых языков. Существуют множество готовых скриптов и инструментов, которые можно адаптировать под свои нужды. А ещё можно воспользоваться ChatGPT для генерации хуков.
Неудобство при командной работе
Миф: Внедрение хуков в командной работе требует согласования и может вызывать недовольство у части команды.
Реальность: Если хук приносит реальную пользу, например, предотвращает ошибки или улучшает качество кода, его использование обычно воспринимается положительно. Важно чётко объяснить преимущества и обучить команду работе с хуками. Для нас это было самым сложным этапом, так как многие изначально не доверяли хукам. Но после того как мы объяснили, что они сократят время выполнения пайплайна и ускорят реакцию на проблемы с код-стайлом, что, в конечном итоге, ускоряет вывод продукта на рынок (TTM), все поддержали их использование.
На этом у меня всё. На тему хуков ещё рекомендую статью Attlassian. Если будут вопросы, то буду рад ответить в комментариях!
Захочешь развивать сервисы вместе с нами — смотри открытые вакансии. А ещё мы делимся опытом в QA и проводим экспертные трансляции в телеграм-канале, заглядывай.