Недавно я впервые внес вклад в новую кодовую базу: расширил и реализовал некоторые функции, которые мне были нужны. После тщательного тестирования того, что функциональность работает должным образом, я сделал коммит. Через несколько минут после этого среда CI передала сообщение:

Провалено 4 теста

Это происходит часто, даже в кодовых базах, с которыми мы привыкли работать. Мы сосредотачиваемся на разработке новых функций и забываем, что существует тест, который их охватывает. Или что есть другой тест, который нужно провести, чтобы охватить новые функции. Сам по себе этот факт не является трагедией, но рабочий процесс в данном случае, безусловно, можно улучшить. Для этого подходят Git Hooks (хуки).

Git Hooks — это скрипты, которые выполняются до или после различных событий Git. Например: commit, push и receive. Они являются встроенным решением (нет необходимости загружать какой-либо сторонний аддон) и выполняются локально на вашем компьютере. Разнообразие сценариев, в которых мы можем их применить, весьма велико.

Давайте рассмотрим предыдущий сценарий. Современный технологический мир имеет тенденцию переходить к решению проблем на ранних стадиях разработки. Например, обнуляемый промис заключается в доставке ошибки во время процесса компиляции, а не во время выполнения. Это имеет много положительных последствий для качества выпускаемого продукта. Аналогично, если можно провалить тесты на нашей машине, мы, несомненно, улучшим рабочий процесс, а не будем терпеть провал теста в среде CI со всеми побочными последствиями (исправление теста на нашей машине, тестирование, повторная отправка, запуск CI).

Предположим, что у нас есть приложение для Android, работающее с Gradle, хотя подойдет любое приложение, работающее на Gradle. Когда запускаются тесты, мы в основном выполняем команду, подобную следующей:

./gradlew clean test

Нам нужно, чтобы эта команда была выполнена до того, как мы отправим код в удаленный репозиторий. Для этого нужно сделать следующее:

1. Перейдите к папке .git/hooks вашего репозитория.

2. Создайте файл с именем pre-push.

3. Скопируйте следующий фрагмент кода:

#!/bin/bash
command="./gradlew clean test"
echo "Executing tests before commit"
$command
result=$?
if [ "$result" -ne 0 ]; then
 echo "Failed execution of tests"
 exit 1
fi
exit 0

Теперь, в любое время, прежде чем вы отправите код, тесты запустятся локально. Если будет найдена какая-либо ошибка, то отправка не состоится (когда скрипт возвращает 1).

Обратите внимание, что вы могли бы делать это на каждом коммите, а не на отправке. В этом случае, вам пришлось бы изменить файл pre-commit. Я считаю, что запуск тестов до отправки более эффективен, чем выполнение его до коммита, но вам, возможно, лучше посмотреть, как это вписывается в рабочий процесс.

Есть несколько интересных идей, которые мы могли бы автоматизировать с помощью Git Hooks. Например, мы можем выполнить статический анализ с помощью таких инструментов, как lint или detekt. Для примера давайте воспользуемся первым и сохраним его в файле pre-commit:

#!/bin/sh

echo "Running detekt"

./gradlew detektCheck
result=$?
if [ "$result" = 0 ] ; then    
   echo "Detekt found no problems"     
   exit 0
else
   echo
   "Problems found, files will not be committed."     
   exit 1
fi

Полный gist: https://gist.github.com/kikoso/a46e6a2efd07ab66ed049b5f7b76ace5

Мы вообще можем объединить эти инструменты и выполнить их вместе на хуке pre-push:

#!/bin/sh

echo "Running detekt"

./gradlew detektCheck
result=$?
if [ "$result" = 0 ] ; then    
   echo "Detekt found no problems"     
   exit 0
else
   echo
   "Problems found, files will not be committed."     
   exit 1
fi


echo "Executing tests before commit"
./gradlew clean test
result=$?
if [ "$result" = 0 ]; then
 echo "Failed execution of tests"
 exit 1
fi
exit 0

Полный gist: https://gist.github.com/kikoso/0a79740c7cde9174ffbafe19caa39306

В любом процессе есть некоторые ручные задачи, про которые легко забыть. Хук Git может гарантировать (или, по крайней мере, напомнить пользователю), что эти задачи должны быть выполнены до того, как коммиты будут фактически отправлены:

#!/bin/sh
read -p "Have the feature been tested? (y/N):" answer
   if [[ "$answer" != "y" ]]; then
      exit 1
   fi
read -p "Have you updated your Jira? (y/N):" answer
   if [[ "$answer" != "y" ]]; then
      exit 1
   fi
read -p "Have you created a test version? (y/N):" answer
   if [[ "$answer" != "y" ]]; then
      exit 1
   fi
read -p "Have you done that task you should have done? (y/N):" answer
   if [[ "$answer" != "y" ]]; then
      exit 1
   fi
  
exit 0

Полный gist: https://gist.github.com/kikoso/d6024e455c733fb91798621ae487a375

Можно применить несколько идей. Например, в зависимости от процесса вы можете создать тег release сразу после того, как объединили свою ветвь разработки с главной. Это просто сделать с помощью хуков Git:

#!/bin/sh
name_of_branch=$(git branch | grep "*" | sed "s/\* //")
if [[ $name_of_branch == "master" ]]; then
	read -p "Branch successfully merged on master. Please, specify tag version and click enter: " version
	echo Creating tag with version "$version"
	git tag -a v$version -m "$version" 
 fi

На самом деле это можно автоматизировать дополнительно (вместо ввода тега на консоли его можно прочитать из файла Gradle или из переменной среды, в которой он хранится).

Это полный список доступных хуков в Git. Мы не будем подробно останавливаться на каждом из них, так как их название говорит само за себя.

Заключение

Хуки Git — это очень эффективный и гибкий механизм для улучшения рабочего процесса. Поскольку они основаны на сценариях оболочки, возможности безграничны. С ними мы можем выполнить практически любую задачу, которую нелегко выполнить с помощью других инструментов.

Имейте в виду, что хуки Git должны быть установлены вручную, они не хранятся в репозитории для всех пользователей. Я рекомендую вам создать папку githooks/ в вашем Git репозитории и хранить их там. Вы даже можете создать скрипт, который все установит, когда репозиторий будет загружен в первый раз (даже включая хук Git, который установит другие, содержащиеся в этой папке хуки, после обновления репозитория).

Читайте также:


Перевод статьи Enrique López-Mañas: Using Git Hooks to improve your development workflow

Предыдущая статья7 ошибок Python, от которых стоит немедленно избавиться
Следующая статья3 фундаментальных постулата JS, приближающих вас к Pro-статусу