Командная работа подразумевает порядок, устоявшиеся процессы и единообразие в подходах. Единая политика именования веток в git определенно следует духу этих тезисов. Разберем каким образом можно добиться единого формата как для локальных веток разработчиков, так и для веток в origin. С этой публикации я начинаю цикл статей о devops-процессах.
Содержание
Политика именования веток в git
Примеры настройки CI/CD будут приводиться на основе GitHub Actions, но я буду стараться предоставить код, который максимально переносим на другие платформы.
Git hooks
По умолчанию git никак не проверяет формат имени веток, за исключением базовой проверки на допустимость тех или иных символов/сочетаний (см. git-check-ref-format). Реализовать подобную проверку можно с помощью git hooks (= хук, перехватчик). Хук – обычный скрипт, который выполняется при наступлении того или иного события (например post- или pre- commit). Бывают серверные и клиентские хуки. Если серверные хуки реализовать на GiHub Actions не представляется возможным, то клиентские полностью в нашей власти.
Тем не менее реализовать логику серверных хуков вполне возможно. Рассмотрим это ниже, а пока создадим наш первый локальный хук.
Настройка локальных хуков
В каталоге .git/hooks хранятся примеры неактивных хуков с расширениями .sample. Чтобы они заработали, это расширение надо убрать или создать новый файл с аналогичным именем без расширения .sample. Имена должны строго соответствовать тем, что уже есть (никакие *.sh, *.py и др. не допускаются). Впрочем shell-скрипты будут работать и так, а изнутри вы можете вызвать уже что угодно. Итак, вот простейший bash-скрипт, который позволяет проверить название ветки:
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/env bash branch_name=$(git rev-parse --abbrev-ref HEAD) branch_name_format='issue-[0-9]*/..*' error_message="Error! Branch name ${branch_name} doesn't fit regex pattern ${branch_name_format}" if [[ ! ${branch_name} =~ ${branch_name_format} ]]; then echo ${error_message} exit 1 fi |
Обратите внимание на переменную:
1 |
branch_name_format='issue-[0-9]*/..*' |
Скриншот ниже наглядно демонстрирует какие имена веток попадают под регулярное выражение, а какие нет:
Вероятно ваши политики будут отличаться, а потому составьте и отладьте свою регулярку. Вам в помощь огромное количество онлайн-сервисов, например regex101.com.
Для проверки работы хука поместите скрипт в файл .git/hooks/pre-commit и выполните команды:
1 2 |
git checkout -b "wrong-branch-name" git commit --allow-empty -m "test commit" |
Ветку создать у вас получится, а вот закоммитить в неё даже пустоту (флаг –allow-empty) git вам уже не разрешит:
1 |
Error! Branch name wrong-branch-name doesn't fit regex pattern issue-[0-9]*/..* |
Выставим ветке корректное имя:
1 |
git branch -m issue-1/demo |
Вот так это работает. Осталось только решить две проблемы. Первая – хуки надо доставить на локальные машины разработчиков, более того, сделать это нужно для каждого репозитория. Вторая – если кто-то решит обойти проверку и пушнуть ветку с кривым именем, то у него это легко получится, ведь контроля на стороне сервера нет. Ниже подумаем как с этим быть.
Доставка локальных хуков
Git это не инструмент контроля соответствия окружения тем или иным политикам, а потому ждать чуда от него не стоит – за локальные копии репозитория отвечает сам разработчик и никто иной. Тем не менее мы можем упросить для него жизнь, написав скрипт начальной подготовки окружения. Воспользуемся всем знакомой утилитой Make, которая часто уже установлена в системе по умолчанию.
Итак, сохраним скрипт проверки имени ветки (см. выше) в отдельный файл:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mkdir -p env/git-hooks cat << 'EOF' > env/git-hooks/pre-commit #!/usr/bin/env bash branch_name=$(git rev-parse --abbrev-ref HEAD) branch_name_format='issue-[0-9]*/..*' error_message="Error! Branch name ${branch_name} doesn't fit regex pattern ${branch_name_format}" if [[ ! ${branch_name} =~ ${branch_name_format} ]]; then echo ${error_message} exit 1 fi EOF |
Далее в корне создаем Makefile со следующим содержимым:
1 2 3 4 5 6 |
cat << 'EOF' > Makefile env: git-hooks git-hooks: cp env/git-hooks/* .git/hooks/ EOF |
Достаточно выполнить команду ниже, чтобы скопировать все необходимые хуки из корня репозитория в каталог ./git/hooks/:
1 |
make env |
Хранить одни и те же хуки в каждом репозитории может показаться не совсем рациональным. Вы вполне можете создать отдельный репозиторий со служебной информацией (в том числе с хуками) и подтягивать данные при необходимости. Задача этой статьи – показать как можно сделать, а усложнить сценарии вы сможете и без меня.
Настройка окружения
Все описанное выше остается на усмотрение разработчика и даже выполнение всего лишь одной команды может быть легко забыто. Очень уместно в данном случае создать файл CONTRIBUTING.md в корне репозитория например с таким содержимым:
1 2 3 4 5 6 7 8 9 10 11 |
# Contribution guide ## Environment Run the following command(s) to configure environment*: ```shell script make env ``` \* Make utility is required |
Ну а в README.md сделать на него отсылку:
1 2 3 |
## Contribution Read [CONTRIBUTING.md](CONTRIBUTING.md) |
Так выглядит уже гораздо лучше, а у вас появится больше порядка.
Проверка на стороне сервера
Проверять имена веток на стороне GitHub можно с помощью отдельного пайплайна CI/CD. Скрипт для проверки можно взять тот же, который использовали выше.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
mkdir -p .github/workflows cat << 'EOF' > .github/workflows/validate-branch.yml --- name: "Validate branch name" on: push: branches-ignore: - main jobs: validate-branch: runs-on: ubuntu-20.04 steps: - run: env - uses: actions/checkout@v4 - name: Validate branch run: | branch_name_format='issue-[0-9]*/.*' error_message="Error! Branch name ${GITHUB_REF_NAME} doesn't fit regex pattern ${branch_name_format}" if [[ ! ${GITHUB_REF_NAME} =~ $branch_name_format ]]; then echo ${error_message} exit 1 fi EOF |
Обратите внимание на список переменных в скрипте – их стало на одну меньше. Мы можем не вычислять самостоятельно имя ветки, ведь GitHub Actions хранит это значение в переменной GITHUB_REF_NAME. Также не забудьте подправить имя ветки, на которой пайплайн запускаться не будет (действительно, зачем проверять main?).
Теперь остается сделать прохождение данного пайплайна обязательным. Для этого заходим в настройки репозитория – Settings / Branches / Branch protection rules. Добавляем правило, в шаблоне веток указываем main и ставим галочку на “Require status checks to pass before merging”. Вот так это выглядит:
А вот так выглядит статус пул реквеста с не прошедшей проверкой (см. Validate branch name):
Теперь обойти проверки уже не получится, если вы конечно не администратор репозитория.
Заключение
В статье мы рассмотрели простейшие сценарии организации процессов совместной работы с репозиториями. Помните, что ваша задача организовать все таким образом, чтобы окружение максимально ограждало человека от совершения ошибок. Если вдруг вам нужна помощь или остались вопросы, вы можете задать их лично мне или вступить в нашу закрытую группу в Telegram. В следующей статье планирую обсудить автоматизацию одного из самых болезненных процессов – управление релизами.