TeamCity как Debian-репозиторий

  • Tutorial

… или использование 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, равно как и необходимые для сборки зависимости для всех четырёх пакетов (из категорий image build-depends и image 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

и в конечном счёте выглядит так:


image


Теперь можно добавить 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, что выясняется только в процессе сборки:

image


Поскольку сервер 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" можно заполнить в один щелчок мыши, не вводя имя каталога вручную:


image


Сборка: разрешение проблем


Качество кода


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


А теперь можно посмотреть на дерево коммитов и порадоваться.

image


  • Вот идёт ветка bash-4.3-testing с тэгами bash-4.3-rc2 и (ниже, не видно) bash-4.3-rc1 — и потом она внезапно обрывается. Если верить истории версий, то релиз bash 4.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. Это означает, что последнее стабильное состояние "bash 4.3 плюс патчи" взять, увы, неоткуда. Слава богу, TeamCity позволяет выполнять сборку по тэгу (флаг "Enable to use tags in the branch specification"), что и было в конце концов сделано.

Резюме:


  • То, что я увидел, не похоже ни на традиционную схему создания веток, ни на git-flow.
  • Да, я в курсе дела, что сборка по тэгу сводит к нулю весь смысл continuous integration, но общаться с разработчиком bash мы будем в другой раз.

Зависимости


При запуске первой же сборки мы увидим, что dpkg-buildpackage завершил работу с кодом возврата 3:


image


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


image


Но вот мы установили всё, что требовалось (на всех агентах), а 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

О других способах самообмана можно почитать здесь.


Финишная прямая


После того, как все круги ада пройдены, мы увидим, что очередная сборка завершилась созданием артефактов:


image


Если вы не хотите, чтобы с каждым инкрементом версии пакета количество артефактов единичной сборки увеличивалось (в конечном счёте предлагая широкий ассортимент из 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. Некоторое неудобство состоит в том, что для каждой конфигурации (читай: программного пакета) приходится добавлять новый фильтр, фактически идентичный предыдущему:


image


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


Каталоги пакетов по архитектурам

и список доступных пакетов

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


Заметно, что у пакетов отсутствует цифровая подпись


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



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


Happy building!

image

Метки:
Поделиться публикацией
Похожие публикации
Комментарии 12
  • +1
    1. Можете объяснить в чем смысл самостоятельно собирать пакеты типа bash?
    2. Смотрели ли вы в сторону http://openbuildservice.org/ который как раз таки заточен под сборку пакетов и интеграцию с репозиториями?
    • 0
      1. bash приведён исключительно для примера. Смысл собирать в том, что
        • Некоторые официальные сборки пакетов, увы, кривые, и их приходится пересобирать.
        • Некоторые пакеты (xfs, X11 Font Server) ранее присутствовали в дистрибутиве, а затем были исключены из него, но по-прежнему представляют интерес для пользователей.
        • Наконец, кто-то может разрабатывать своё ПО вне инфраструктуры Debian, но ориентироваться на одну конкретную ОС. Использовать для этих целей Open Build Service — перебор.
      2. Open Build Service — безусловно, хорошая штука, но так сложилось, что в своей работе я уже использую TeamCity. "Боевого" опыта использования Open Build Service у меня нет, так что сделать полноценное сравнение я не смогу.
    • +1

      Беда таких систем сборки конечных пакетов в следующем (мой опыт): нет пересборки по древу зависимостей. (сделать можно, но дикое усложнение и деньги, в случае TC/Bamboo). Сие ведет, в частности, к тому, что тяжело контролировать версии (когда их нужно много). Если пакетов мало, то с OBS нет никакого смысла заморачиваться. В противном случае, проще отправлять таском из CI в OBS.

      • +1
        Статья обалденная. Но в ней, кроме сисек, не раскрыт еще один важный момент: debian/changelog. Как он меняется? Что происходит в случае Team Upload (dch --team)? Что в случае QA Upload(dch --qa)? NMU? Как автоматически отображать релиз в ченджлоге (dch -r)?
        • 0

          Спасибо за оценку.


          На вопрос, думаю, смогу ответить спустя какое-то время, когда наберётся достаточная статистика по dpkg (пока что не было ни одного NMU или QA Upload).


          Пока что вижу, что


          • не всякий коммит сопровождается записью в debian/changelog, что странно
          • имена авторов изменения в VCS и в debian/changelog изредка различаются, что тоже странно

          Думаю, надо внимательно курить специфичную для Debian литературу (Policy Guide и т. д.).


          Могу порекомендовать Вам поднять сборку dpkg, используя статью в кач-ве инструкции, и убедиться во всём воочию.

          • +1
            А вот в этом то и проблема. Запись в ченджлоге должна быть не с каждым коммитом (может там всего лишь identetion поправили), а одна запись на одну story или один bugfix. То есть законченная задача, связанная не столько с системой контроля версий, сколько с issue-трэкером — значит он тоже должен быть в связке.
            • 0

              Похоже, пора переводить статью на английский и писать в debian-devel@lists.d.o с просьбой прокомментировать.

              • 0

                Андрей Рахматуллин из проекта Debian комментирует:


                If you use dpkg-buildpackage then the specifics of the last changelog entry don't matter.
                • +1
                  Doesn't matter потому что он говорит про «entry». Но версию то менять надо. На ее основе будет собран пакет с этой версией и отправлен в репозиторий. Если версию не поменять автоматически, то он перетрет старый пакет в репо. В результате все машины, настроенные на этот репозиторий выкинут ошибку о неконсистентной чексумме. А даже если им всем сделать apt-get clean/update, то все равно апдейтиться на эту версию они не станут, так как у них уже эта версия стоит (хотя по факту она предыдущая).
                  • 0

                    Я понял.


                    Т. е. вопрос скорее в контексте не continuous integration, а continuous delivery. Тогда это не вопрос ко мне или к разработчикам Debian, а задача для автора tcDebRepository, причём имеющая смысл исключительно для сборки пакетов из основной поставки Debian GNU/Linux.


                    Если я собираю свой пакет на своём сервере, я не вижу проблем в том, чтобы просто сделать инкремент версии в случае, эквивалентном, скажем, binNMU.


                    Или я что-то упустил?

                    • +1
                      Да, это откровенно continuous delivery (даже если это деливери в QA). Но .deb — это вообще continuous delivery всегда по определению :)

                      По поводу своего пакета — тогда врядли актуально. Я тоже собираю свои пакетики для инфраструктуры. Changelog меняю руками. Вполне хватает рук, автоматика не нужна. Все вышесказанное актуально для пакетов основного приложения, которое тестируют, деплоят и обновляют. Где важен конвейер.
          • +1

            Добрые у Вас названия агентов, однако. :-)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.