… или использование TeamCity для сборки *.deb-пакетов и не только.
Написат�� статью меня побудило знакомство с модулем tcDebRepository. Я наивно полагал, что "вот сейчас я его подключу, и всё волшебным образом заработает". Как водится, не заработало, и в конце концов был накоплен некий опыт, который захотелось систематизировать.
Статья ни в коей мере не является введением в основы TeamCity и предполагает, что читатель уже знаком и собственно с TeamCity, и с инфраструктурой Debian GNU/Linux. Если вы уже представляете, что такое continuous integration, но ещё ни разу не держали в руках TeamCity — вам, наверное, сюда. О сборке пакетов в Debian можно почитать в Debian New Maintainers' Guide.
Для игр (на случай, если кто-то захочет воспроизвести результаты) использовался сервер TeamCity 10 и 3 агента под управлением Debian 8.0 (Jessie). 3 агента — это лимит в случае TeamCity Professional. Всё ниженаписанное, думаю, без проблем переносится на любой другой дистрибутив на основе Debian GNU/Linux, напр., Astra Linux.
Планы
Достаточно произвольным образом я выбрал для экспериментов 4 пакета:
С учётом ограничения лицензии типа Professional на количество конфигураций сборки можно было "набрать" до 20 пакетов.
Подготовка
TeamCity загружается с официального сайта. Кроме собственно TeamCity, на каждую из агентских машин нам потребуется установить пакет build-essential, равно как и необходимые для сборки зависимости для всех четырёх пакетов (из категорий
build-depends и
build-depends-indep). Это позволит минимизировать (но совсем не обязательно устранить) проблемы с зависимостями при сборке.
Виды пакетов в Debian GNU/Linux
Пакеты, помимо прочих особенностей, делятся на "родные" (native) и внешние (non-native) (подробнее). "Родные" пакеты (autotools-dev, debhelper, dpkg) обычно разрабатываются в рамках проекта Debian, и исходный код уже содержит необходимую для сборки метаинформацию (каталог debian/ в корне дерева исходного кода).
Отличие внешних пакетов (bash) в том, что исходный код никоим образом не завязан на Debian, и инженерам сопровождения (в русскоязычной документации это называется "разработчик Debian", в англоязычной — просто "maintainer") приходится поддерживать параллельное дерево исходного кода с метаинформацией и патчами (это то самое содержимое каталога debian/).
Общие настройки
Бинарные пакеты, которые мы будем собирать — это, в терминологии TeamCity, "артефакты". Соответственно, нужно указать, что мы ожидаем иметь в сухом остатке по окончании очередной сборки, указав artifact paths:

Для "родных" пакетов артефакты pkgname.orig.tar.{gz,bz2,xz} и pkgname.debian.tar.{gz,bz2,xz} не создаются.
Подключение исходного кода к TeamCity
Чаще всего как раз с этим шагом нет ничего сложного: просто идём в настройки конфигурации сборки (build configuration) и добавляем новый корень системы контроля версий (VCS root). Для "родных" пакетов эту операцию нужно выполнить однократно, для внешних — как правило, дважды (но возможны исключения, когда и разработчики (вне проекта Debian), и инженеры сопровождения используют одну и ту же DVCS (Git, Bazaar), и изменения в коде постоянно "кочуют" из одного репозитория в другой, в то же время не вызывая merge-конфликтов для метаинформации и патчей).
Единственная особенность состоит в том, что артефакты в нашем случае будут собираться вне дерева исходного кода (на один каталог выше), так что нам нужно настроить checkout rules таким образом, чтобы, скажем, исходный код пакета dpkg выгружался не в текущий рабочий каталог, а в одноимённый пакету подкаталог, т. е. dpkg/. Это достигается добавлением в checkout rules одной строчки:
+:.=>dpkgи в конечном счёте выглядит так:

Теперь можно добавить VCS-триггер и к настройке контроля версий уже не возвращаться:

Интеграция с Bazaar
"Но ведь для сборки bash требуется интеграция с Bazaar, а штатная поставка TeamCity не поддерживает эту систему!" — скажет внимательный читатель, и будет прав. Кроме того, TeamCity, увы, не позволит нам добавить и Git URL вида bzr::http://bazaar.launchpad.net/~doko/+junk/pkg-bash-debian — у JGit слишком много ограничений.
Существует внешний модуль для поддержки Bazaar, но у него есть по меньшей мере два серьёзных недостатка:
- checkout на стороне агента не поддерживается, и
- штатной поставки Bazaar недостаточно, т. к. модуль опирается на функциональность bzr xmlls и bzr xmllog, что выясняется только в процессе сборки:

Поскольку сервер TeamCity у меня работал на Windows, я отказался от весёлого приключения в виде установки Bazaar на стороне сервера, а вместо этого в случае пакета bash просто добавил ещё один шаг сборки (Bazaar-интеграцию для бедных), используя Command Line Runner и следующий сценарий оболочки:
#!/bin/bash
#
# vim:ft=sh:
#
export LANG=C
export LC_ALL=C
set -e
rm -rf bash/debian
bzr branch http://bazaar.launchpad.net/~doko/+junk/pkg-bash-debian bash/debian
major_minor=$(head -n1 bash/debian/changelog | awk '{print $2}' | tr -d '[()]' | cut -d- -f1)
echo "Package version from debian/changelog: ${major_minor}"
tar_archive=bash_${major_minor}.orig.tar
rm -f ${tar_archive} ${tar_archive}.bz2
# +:.=>bash checkout rule should be set for the main VCS root in TeamCity
tar cf ${tar_archive} bash
tar --delete -f ${tar_archive} bash/debian
# Required by dpkg-buildpackage
bzip2 -9 ${tar_archive}Такой подход не позволит нам "видеть" изменения в одном дереве исходного кода (из двух) и автоматически запускать сборку при их (изменений) появлении, но для первого опыта вполне достаточен.
N.B.! Поскольку Command Line Runner не умеет подсвечивать синтаксис кода сценария, для пользователей браузеров Mozilla Firefox и SeaMonkey я бы рекомендовал расширение It's All Text!, позволяющее редактировать содержимое текстовых полей во внешнем редакторе. Можно подключить Vim или Emacs и насладиться подсветкой синтаксиса, автодополнением, шахматами и поэтессами.
Сборка: настройка
Для сборки нам достаточно использовать уже знакомый нам Command Line Runner, вызывающий dpkg-buildpackage. Ключи -uc и -us означают, что мы не хотим создавать цифровых подписей для наших пакетов. Если всё-таки хотим — придётся загрузить соответствующую пару GnuPG-ключей на каждый из агентов.
Также обратите внимание, что dpkg-buildpackage должен исполняться не в текущем рабочем каталоге, а в одноимённом пакету подкаталоге (куда будет выгружено дерево исходного кода). Если настройка контроля версий выполнена, поле "Working directory" можно заполнить в один щелчок мыши, не вводя имя каталога вручную:

Сборка: разрешение проблем
Качество кода
Как ни странно, но качество кода (или, точнее, стиль разработки) может являться серьёзной проблемой на пути внедрения continuous integration. Опытным путём выяснилось, что, в случае bash, версии в двух деревьях кода рассинхронизированы: последние коммиты в основном дереве соответствуют версии 4.4, хотя файл debian/changelog уже без малого два года назад остановился на версии 4.3, и код одной версии с метаинформацией другой версии вместе не собираются. Хорошо, значит, мне нужна ветка bash-4.3 в основном дереве.

- Вот идёт ветка
bash-4.3-testingс тэгамиbash-4.3-rc2и (ниже, не видно)bash-4.3-rc1— и потом она внезапно обрывается. Если верить истории версий, то релизbash4.3 так и не состоялся. - В то же время, спустя несколько дней на ветке
masterпоявляется коммит с тэгомbash-4.3, которому не предшествует ни одна операция типа merge или cherry-pick. - Беглый взгляд на историю и содержание коммитов приводит к ощущению, что вся разработка ведётся в локальной ветке одного человека, а
git pushна savannah.gnu.org происходит через равные промежутки времени, причём черезgit merge --squash -s ours(у каждого коммита невероятно длинный и трудно читаемыйdiff). - Коммиты "
Bash-4.3 patch XY" (всего 46 патчей для версии 4.3) кладутся вmaster(наbash-4.3-testingих нет), а через 3 недели на веткеmasterпоявляется меткаbash-4.4-beta2. Это означает, что последнее стабильное состояние "bash4.3 плюс патчи" взять, увы, неоткуда. Слава богу, TeamCity позволяет выполнять сборку по тэгу (флаг "Enable to use tags in the branch specification"), что и было в конце концов сделано.
Резюме:
- То, что я увидел, не похоже ни на традиционную схему создания веток, ни на git-flow.
- Да, я в курсе дела, что сборка по тэгу сводит к нулю весь смысл continuous integration, но общаться с разработчиком
bashмы будем в другой раз.
Зависимости
При запуске первой же сборки мы увидим, что dpkg-buildpackage завершил работу с кодом возврата 3:

В результате просмотра протокола сборки выяснится, что какие-то зависимости всё-таки отсутствуют:

Но вот мы установили всё, что требовалось (на всех агентах), а dpkg-buildpackage завершается с тем же кодом. В чём же дело? Здесь есть несколько нюансов.
- Скорее всего, вы будете собирать ПО из Debian unstable или Debian experimental. В таком случае, для удовлетворения необходимых для сборки зависимостей агенты TeamCity тоже должны работать под управлением Debian unstable или Debian experimental (иначе
dpkg-buildpackageбудет "ругаться", что ваши стабильные версии зависимостей "устарели"). Для п��давления ошибки иногда достаточно добавить ключ -d:
dpkg-buildpackage -uc -us -d - Частным случаем устаревших зависимостей является сценарий
configure, созданный более новой версией GNU Autotools, чем в настоящее время установлены в системе.dpkg-buildpackageне в состоянии диагностировать такую ситуацию — вместо этого в протоколе сборки мы наблюдаем загадочные сообщения об отсутствующих макросахm4. Решением является повторное создание сценарияconfigureс помощью текущей версии GNU Autotools. Просто добавьте первым шагом сборки следующую команду:
autoreconf -i
"Сломанные" unit-тесты
Если мы всё-таки хотим себя обмануть и таки собрать наш пакет, достаточно будет запустить dpkg-buildpackage в изменённом окружении:
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -uc -usО других способах самообмана можно почитать здесь.
Финишная прямая
После того, как все круги ада пройдены, мы увидим, что очередная сборка завершилась созданием артефактов:

Если вы не хотите, чтобы с каждым инкрементом версии пакета количество артефактов единичной сборки увеличивалось (в конечном счёте предлагая широкий ассортимент из dpkg_1.18.16_i386.deb, dpkg_1.18.17_i386.deb и dpkg_1.18.18_i386.deb), содержимое рабочего каталога стоит выборочно очищать перед каждой сборкой. Это можно было бы выполнить вручную, непосредственно перед dpkg-buildpackage вызвав rm -rf c пресловутыми artifact paths в качестве аргументов, но есть способ лучше — штатный модуль TeamCity с ласковым названием "Швабра". Вот так выглядят его настройки (ключевое здесь — "Before next build start"):

А вот таким образом будет выглядеть соответствующий фрагмент протокола сборки, если модуль Swabra правильно настроен:

Теперь самое время настроить наш Debian-репозиторий. Это достигается добавлением артефакт-фильтров в настройках модуля tcDebRepository. Некоторое неудобство состоит в том, что для каждой конфигурации (читай: программного пакета) приходится добавлять новый фильтр, фактически идентичный предыдущему:

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


При добавлении репозитория в /etc/apt/sources.list можно наблюдать все те же пакеты уже со стороны клиента:


N.B.! Если вы собираете под несколько архитектур (i386, x32, amd64, arm), стоит либо иметь несколько отдельных конфигураций сборки, соответствующих одному пакету и различающихся требованиями к агентам, либо, в дополнение к VCS Trigger, добавить Schedule Trigger с флагом "Trigger build on all enabled and compatible agents":

Через какое-то время вы увидите, что проект dpkg активно развивается, а вот остальные участники, похоже, курят бамбук.

