Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Сегодня почти в каждом проекте существует хотя бы базовая инфраструктура, включающая в себя систему хостинга кода проекта (Github, Bitbucket и др.) и систему его сборки (Teamcity, Jenkins и др.). Мы в ЦФТ активно используем Bitbucket server и Teamcity в наших пайплайнах. Под катом я расскажу, как мы настроили взаимодействие этих сервисов, с какими трудностями столкнулись и как с ними справились.
Обычно взаимодействие систем хостинга кода проекта и системы его сборки очень простое: на каждое изменение в коде проекта, которое появляется в системе хостинга кода, должна происходить его сборка в системе сборки. То есть в нашем случае при каждом пуше в Bitbucket должна происходить сборка на Teamcity. Для этого нужно настроить VCS Roots конфигурации на чтение всех изменений в репозитории:
И настроить Trigger конфигурации на запуск билда на любое изменение в репозитории:
Поздравляю, ваш пайплайн готов! Теперь вы и ваша команда всегда будете в курсе, что новые изменения в проекте всё ещё позволяют ему собираться. И всё круто, пока количество разработчиков в проекте немного и отправка изменений в Bitbucket не идёт каждую минуту. Но бывает, что в проекте несколько десятков разработчиков, и работа идёт одновременно в таком же количестве веток. Такая нагрузка приводит к снижению производительности системы сборки.
Решить эту проблему можно двумя способами:
- закупить много билд агентов с впечатляющими характеристиками;
- провести оптимизацию пайплайна.
Понятное дело, что добавлять ресурсы для того, чтобы изменения сразу получали свои билды — это серьёзные финансовые затраты. Такой подход при расширении вашей команды будет выглядеть так:
Поэтому стоит рассмотреть вариант с оптимизацией пайплайна. Для этого стоит ответить на пару вопросов:
- можем ли мы меньше писать кода? Естественно, нет.
- можем ли мы собирать проект не на каждое изменение? Возможно.
В обычном пайплайне проекта имеется основная ветка develop, в которую разработчики периодически сливают изменения из рабочих веток с фичами, багфиксами, техдолгами и т.д. Коммиты в рабочие ветки часто бывают промежуточными и не нуждаются в проверке на успешный статус сборки, поэтому можно не запускать сборки на такие изменения. Перед самим влитием рабочей ветки в основную обычно создаётся пул реквест, который проверяют другие разработчики. Сам факт создания пул реквеста означает, что изменения готовы попасть в основную ветку и необходимо проверить их статус сборки. Таким образом, можно сэкономить на ресурсах и собирать проект только на каждый пул реквест.
Как собирать проект только на пул реквестах?
Для того чтобы собирать проект только на изменения в пул реквестах, мы решили использовать ссылки в git на пул реквесты в Bitbucket. Для этого нужно изменить VCS roots конфигурации сборки проекта на Teamcity:
Таким образом, мы говорим Teamcity, что нужно обновлять информацию только по git ссылкам на пул реквесты, которые создаёт Bitbucket.
Красота! Теперь сборка проекта запускается только, если изменения произошли в ветке, на которую создан пул реквест. В Teamcity ветка будет иметь название — номер пул реквеста в Bitbucket.
И всё, казалось бы, хорошо: билд агенты целы, разработчики сыты. Но это длилось недолго...
Периодически мы начали замечать, что сборки проекта на пул реквесты стали запускаться с задержкой, которая могла быть до нескольких часов. Разумеется, это непозволительная погрешность, которая ломала нам весь пайплайн. Мы заметили, что Teamcity не сразу видит пул реквесты, созданные на Bitbucket. Проверив локально git ссылки, оказалось, что ссылки на пул реквесты появляются с такой же задержкой, что и на Teamcity. Мы решили узнать, почему Bitbucket плохо обновляет git ссылки на пул реквесты, и оказалось, что мы не первые столкнулись с такой проблемой.
Если быть кратким, то Bitbucket говорит, что не стоит использовать git ссылки на пул реквесты, так как это их внутреннее API.
Мы поняли, что вариант с git ссылками больше нам не подходит, но и возвращаться к огромной очереди на сборку мы не хотим, поэтому пришлось снова думать, как спасти систему сборки от сильной загрузки. Копать мы решили в сторону публичного API Bitbucket server.
В публичном API мы нашли методы, которые могут отдавать список пул реквестов по разным параметрам. В том числе и по id коммита. И раз Teamcity не может узнавать из git ссылок о наличии пул реквеста для полученного коммита, то может узнать это, используя публичное API Bitbucket server.
То есть необходимо реализовать в Teamcity такой процесс:
В Teamcity есть механизм Dependencies, который позволяет создавать цепи конфигураций и настраивать стратегию поведения конфигураций в этих цепях. Значит, мы можем создать конфигурацию, которая будет получать абсолютно все изменения с Bitbucket и заканчиваться успешно, если пул реквест на такой коммит существует, и завершаться с ошибкой, если пул реквеста на такой коммит нет.
Мы назвали такую конфигурацию CheckPR. Настроили VCS roots на получение всех изменений с Bitbucket:
И добавили один Build step типа Command Line с bash скриптом с запросом к Bitbucket API, который получает всё пул реквесты на этот коммит и проверяет — есть ли открытый:
<![CDATA[#!/bin/bash
pr=$(curl -u "login:password" https://your.hostname.ru/rest/api/1.0/projects/PROJECT/repos/repository/commits/%build.vcs.number%/pull-requests)
if [[ $pr != *"\"state\":\"OPEN\""* ]]; then
exit 1
fi]]>
Осталось настроить саму конфигурации сборки. VCS roots и Trigger настраиваем точно так же как и в начале статьи, но добавляем настройку параметра Dependencies:
Ставим зависимость от конфигурации CheckPR и устанавливаем условия для закрытия конфигурации при ошибке в конфигурации CheckPR. Это позволит просто выкинуть конфигурацию из очереди и не начинать её собирать, если на этот коммит нет пул реквеста. Это победа!
Но вы не поверите...
Всё работало супер, но не для всех пул реквестов. Иногда Bitbucket API нас обманывало и говорило, что для коммита нет никакого пул реквеста, хотя он был. Тогда мы решили изменить подход, используя всё то же Bitbucket API.
Раз пул реквест действительно существует, то мы стали получать список всех пул реквестов и искать в них коммит, для которого сработал запуск конфигурации Teamcity. Для этого мы просто изменили bash скрипт в Build Step конфигурации CheckPR:
#!/bin/bash
pr=$(curl -u "login:password" https://your.hostname.ru/rest/api/1.0/projects/PROJECT/repos/repository/pull-requests)
if [[ $pr != *"\"fromRef\":{\"id\":\"%teamcity.build.vcs.branch.TestVerification%\",\"displayId\":\"%teamcity.build.branch%\",\"latestCommit\":\"%build.vcs.number%\""* ]]; then
exit 1
fi
Это дало нам 100% результат. Вместо того чтобы собирать проект на каждый коммит и тратить на сборку 20-30 минут билд агента, мы тратим максимум 10 секунд, чтобы проверить необходимость занимать эти 20-30 минут сборки.