Практические приёмы снижения копипасты и упрощения конфигурирования множества сходных проектов в TeamCity.
Статья о мышко-клацательном варианте конфигурирования TeamCity.
Большое число билд-конфигураций сложно поддерживать: проблематика та же, что описывается в принципе DRY программирования. Изначально одинаковое подвергается точечным изменениям, в итоге неочевидно, как конкретно работает каждая конфигурация в отдельности, и системное изменение проблематично произвести. Ниже приведу несколько простых приёмов, которые позволяют существенно снизить нагрузку на поддержку однотипных конфигураций.
Примеры будут про сборку C#-проектов, но специфика не особенно влияет на описываемые приёмы.
Встроенные переменные
Структура проекта и солюшна как правило предсказуема:
MySuperApp /src MySuperApp.csproj /test MySuperAppTests.csproj MySuperApp.sln
Если же структура от проекта к проекту у вас заметно различается, то предложил бы начать с этого — стандартизировать оформление исходников.
В примере видно, что имена директорий, проектных файлов либо совпадают с именем проекта, либо используют его как префикс со стандартным суффиксом. Если всё это хранится не в монорепе, то, скорее всего, и GIT-репозиторий называется так же. И компонент либо тег в таск-трекере. И конфиг в инструменте деплоя.
Если выделить это имя как переменную, то структура однотипных проектов, как и набор команд для работы с ними, окажутся идентичными и легко параметризуемыми. Достаточно и билд-конфигурацию в TeamCity назвать так же, как проект — «MySuperApp» в рассматриваемом примере.
TeamCity позволяет использовать автоматически наполняемые переменные сборки, чтобы получать информацию о ней. Так можно получить имя конфига:
PROJECT_KEY = %env.TEAMCITY_BUILDCONF_NAME%
а через этот параметр можно определить другие пользовательские параметры, например:
SOLUTION_NAME = %PROJECT_KEY%.sln TEST_PROJ = %PROJECT_KEY%Tests.csproj APP_BINARY_FILE = %PROJECT_KEY%.exe OCTOPUS_DEPLOY_CFG = %PROJECT_KEY% SRC_REPO = %GIT_STORAGE_URL%/%TEAM_SPACE%/%PROJECT_KEY%
соответственно, и команды в шагах сборки можно будет переписать с ad-hoc копипастовых dotnet build MySuperApp.sln на универсальное dotnet build %SOLUTION_NAME%.
Другие полезные и часто используемые переменные:
teamcity.build.checkoutDir - куда тимсити клонировал исходники teamcity.build.branch - клонированная ветка VCS (GIT) teamcity.build.branch.is_default - главная ли ветка собирается teamcity.build.step.name - имя данного шага конфига system.teamcity.build.tempDir - временная папка под конкретный билд
Временная папка полезна тем, что checkoutDir всё же задумана как кешируемая, поэтому одноразовые вещи туда лучше не складывать; времянка живёт ровно до завершения конкретного билда. Расположения разных папок важно знать, если придётся куда-то передавать полный путь к файлам.
Комбинируя такие параметры, можно налету, а не в процессе конфигурирования сборки, много чего узнавать о текущем проекте, окружении, обстоятельствах сборки. Системные параметры лучше предварительно заворачивать в свои и затем уже свои параметры рассовывать по шагам и в конкретные команды — так проще централизованно добавить суффикс или вроде того.
Параметризация
Задавать параметры конфигурации можно на нескольких уровнях:
ROOT /PROJECT ( /TEMPLATE ) /BUILD_CONFIG
с каждого верхнего уровня параметры наследуются, проваливаются вниз. Таким образом, в конкретном билд-конфиге доступны параметры уровня группы конфигов (проекта тимсити) и общие для инстанса.
Очевидно, что для множества, если не всех проектов команды используется один и тот же таск-трекер, VCS и много чего ещё. Такие параметры уместнее задавать не в каждой конфигурации сборки, а один раз на уровне сервера. Приём примитивный, тем не менее сильно упрощает ситуацию с копипастой.
На уровне «проекта» можно задать общие для конфигов параметры, обусловленные тем же принципом, по которому конфиги оказались объединены в одну группу. Здесь же можно переопределить ROOT-параметры, и это переопределение провалится вниз до конфигов.
Некоторые проекты кажутся невероятно уникальными, поскольку в них не должен выполняться некий шаг или, наоборот, в определённых обстоятельствах нужно совершить дополнительное действие. Условное выполнение вполне предусмотрено в TeamCity, вот простейший пример:

в эти условия можно вставлять и свои кастомные параметры.
Напомню, что значения параметров можно переопределять налету с помощью служебных текстовых команд, отправляемых в консоль:
##teamcity[setParameter name='env.MY_VAR' value='hello-world']
так, можно на предварительном «look around» этапе понять, что за проект, каковы обстоятельства сборки, при необходимости дописать что-то в пользовательские параметры, от которых зависит включение либо выключение шагов, и так получится конфигом управлять налету. Это снова расширяет взгляд на возможности стандартизации подхода к сборке множества проектов.
Шаблоны
Кусочки конфигов с несколькими шагами про одно и то же имеют больше шансов на переиспользование, чем весь конфиг целиком. Развивать, дорабатывать и устранять проблемы проще тоже в конкретном компоненте с ограниченной областью ответственности, чем конфиг на 100500 шагов. И TeamCity предоставляет такой инструмент — шаблоны (templates).
Шаги, составляющие по смыслу одно целое, имеет смысл выделять в шаблон. А для конечных конфигов применять уже «крупноблочную сборку» из нескольких шаблонов:

На скриншоте приведён пример конфига на 42 шага, причём кастомных шагов в нём нет совсем, все унаследованы из шаблонов.
При добавлении шаблона в конфиг, все шаги и параметры наследуются кофигом. Шаги при этом управляются как единый блок: если в примере на скриншоте передвинуть любой шаблон выше или ниже, то все его шаги встанут, соответственно, выше или ниже шагов шаблона, который он сместил. Если внести изменения в шаги или параметры на уровне шаблона, то эти изменения «провалятся» в каждый конфиг, использующий этот шаблон.
Контроль
В реальности обстоятельства вынуждают заниматься отладкой, устранением сбоев, поиском решений нестандартных задач. Так возникают вре́менные и не очень отключения шагов, оверрайды команд и параметров, кастомные ad-hoc шаги. Вре́менное всё же должно таким и оставаться, а конфигурирование сборок — стремиться к стандартизации.
Для обнаружения де-факто возникших аномалий можно использовать REST API TeamCity.
Схема взаимодействия такая:
получить список проектов;
в каждом проекте получить список конфигов;
для каждого конфига запросить его XML-определение;
в каждом конфиге осмотреть шаги, триггеры, фичи, VCS.
Аномалией (или ad-hoc вмешательством) является всё что не имеет признака inherited либо он выставлен в false, всё что имеет признак disabled = true. Если шаг задизаблен в шаблоне, то он в наследующих конфигах не отобразится и в XML не попадёт.
Пример запроса для получения определения конфига:
GET /app/rest/buildTypes/id:MySuperApp
Зашедулили в том же TeamCity, направили на почту — своевременно отреагировали, вернули в стойло.
Итого
Простые практики позволяют заметно упростить и систематизировать управление билд-конфигами в TeamCity:
стандартизовать оформления исходников, проектов, конфигов;
использовать встроенные переменные тимсити;
размещать кастомные параметры на подоходящем уровне;
применять тотальную шаблонизацию;
отлавливать аномалии.
Так, вполне можно приблизиться к нулевому конфигурированию проектов: скопировали существующий конфиг, переименовали — готово. Да, это всё ещё копия, но абсолютно лишённая кастомизации, и при внесении изменений в шаблоны, параметры уровня сервера, она эти изменения унаследует. Собственно, к чему и стремились.

