Дано
- Одна маленькая, но очень полезная библиотека. Включает в себя общую функциональность — логирование, работа с Windows Azure, и т.д.
- Большое количество проектов(solutions), где используется данная библиотека.
- Распределённая команда разработчиков, часть которой библиотеку пишет и поддерживает, а другая часть только пользуется.
Проблемы, которые хочется решить
- Необходимость копировать из проекта в проект исходники/бинарники — неудобно, долго, велика вероятность ошибки при обновлении.
- Невозможность использования разных версий для разных проектов — поиск и сборка конкретной версии «из прошлого» неудобны, опять же велика вероятность ошибки при обновлении.
- Необходимость следить за актуальностью зависимостей библиотеки — особенно это касается Azure SDK, который сейчас регулярно обновляется, не всегда у всех разработчиков стоит последняя версия, и обновление SDK не всегда возможно.
- Использование существующего проекта на разных машинах — ещё одно «тонкое» место, порождающее много ненужных ошибок. Для корректной работы необходимо полное совпадение путей для проектов, чего очень сложно добиться.
Способ решения и возникшие проблемы
Сразу стало понятно, что получение пакета с последней/конкретной версией библиотеки при сборке какого-либо решения проще всего сделать через NuGet — работа с Azure SDK располагает к подобному подходу.
Однако после развёртывания репозитория выяснилось, что просто так публикацию в NuGet настроить не получится — пришлось писать отдельный проект, который бы собирал пакет для публикации и заливал бы его в хранилище.
Так же выяснилось, что номер версии в файле AssemblyInfo
не очень удобен — нумерация по умолчанию не включает в себя дату публикации, что несколько затрудняет решение проблем, появляющихся после обновления библиотеки (не всегда легко можно отловить, когда именно перестала работать та или иная часть функционала).
В итоге было решено перенести процесс публикации на TFS server, добавив Build Definition(билд) для библиотеки. Все указанные действия производились в Team Explorer VS 2010, но особых различий при переходе на VS 2012 я не заметил.
Поиск лучшего
В качестве вспомогательного проекта был выбран NuGetter (для автоматической публикации в репозиторий во время билда на сервере) с добавлением TFSVersioning (для редактирования файла AssemblyInfo
во время билда на сервере), автор у проектов один, и проблем с интеграцией не должно было возникнуть.
Пошаговое описание личного опыта внедрения
Хотел бы сразу отметить, что всё написанное ниже — следование документации из обоих проектов, плюс описание некоторых side-эффектов, с которыми я столкнулся.
Раньше настраивать билд лично мне не приходилось, так что все шаги будут описаны достаточно подробно и занудно.
0. Подготовка к настройке — заливка на сервер.
Оба проекта содержат библиотеки с определёнными Custom Actions для рабочего процесса билда на сервере и xaml-файлы с шаблонами рабочего процесса (Workflow), расширяющего возможности билда по умолчанию. Всё это требуется залить на сервер: шаблоны проектов для возможности выбрать их при создании нового билда, а библиотеки — для возможности их найти при очередном проведении этого самого билда.
Шаблоны рабочих процессов рекомендуется выложить в папку для хранения шаблонов по умолчанию $/(Solution Name)/BuildProcessTemplates
.
Библиотеки и файл NuGet.exe с его настройками рекомендуется положить в отдельную папку (на нашем сервере она имеет очень оригинальное название .nuget
— зато всегда вверху, что удобно при настройке билда).
Для того чтобы данные библиотеки были найдены в процессе билда, требуется настроить контроллер билдов (Build Controller) для данного решения. Делается это так:
Team Explorer --> (Solution Name) --> Builds --> правый клик --> Manage Build Controllers...
Выбор контроллера:
Редактирование контроллера для (Solution Name):
В поле Version control path to custom assemblies
требуется указать путь к вашей папке с общими библиотеками.
Здесь у меня возникла первая маленькая проблема — я пытался редактировать свойства не контроллера, а одного из его агентов сборки. Будьте внимательны.
На всякий случай проверьте подключение к папке (кнопка Test Connection
).
После проверки подключения сохраните изменения.
1. Добавление нового билда на основе шаблона.
Теперь нужно добавить новый билд по шаблону, который был загружен в предыдущем шаге. Делается это так:
Team Explorer --> (Solution Name) --> Builds --> правый клик --> New Build Definition...
Название и описание билда:
Расписание для запуска билда:
Варианты:
- Запуск только вручную.
- После каждого check-in от разработчиков.
- Ждать завершения предыдущего билда.
- Собирая check-in, с возможностью поставить время запуска «не чаще чем N минут».
- Принимать check-in только если был удачный merge и сборка на сервере после этого была успешной.
- По расписанию.
Код, который должен быть собран во время билда:
Иногда здесь присутствует слишком много проектов — удалите лишние. Так же можно указать какой проект в какую папку должен быть скопирован во время билда.
Определение параметров по умолчанию для билда:
Необходимо выбрать:
- Контроллер, который был настроен в предыдущем пункте.
- Куда нужно поместить код:
- Не копировать результат сборки никуда.
- Скопировать результат сборки в общую папку
Требуется корректный UNC-адрес вида\servershare
. - Скопировать результат сборки в папку TFS-сервера
Вариант не всегда доступен, в зависимости от прав пользователя, создающего билд.
Настройки билда:
Первые три пункта — это настройки, доступные всегда и для любого билда. Сейчас для нас представляет интерес только первый блок — выбор проектов или решений для сборки (Projects to Build
).
Так же стоит обратить внимание на Build number format
— именно этот параметр отвечает за имя папки с результатом билда (для проектов с большой вложенностью папок должен быть не очень длинным).
Настройки сохранения результатов билдов:
2. Выбор шаблона для билда.
На вкладке Process
в верхней части в выпадающем списке нужно выбрать требуемый шаблон. Если в выпадающем списке не видно нужного, значит, он будет использоваться в первый раз, и его необходимо «показать» серверу с помощью кнопки New...
(выбрать или скопировать уже загруженный на TFS-сервер файл). Пудинг, это — Алиса, Алиса, это — пудинг.
Выбор шаблона:
Варианты:
- [Все установленные на TFS-сервере шаблоны билдов].
VersioningBuildTemplate.xaml
— базовый шаблон для замены версий в файле AssemblyInfo.cs.VersioningBuildTemplate15.xaml
— шаблон для замены в файле AssemblyInfo.cs — дополнительные возможности редактирования свойств библиотеки, доступные в версии TFSVersioning 1.5.NuGetterStandardBuildTemplate.xaml
— базовый шаблон для публикации результатов билда в NuGet.NuGetterVersioningBuildTemplate.xaml
— базовый шаблон для публикации результатов билда в NuGet и замены версий в файле AssemblyInfo.cs.NuGetterVersioningBuildTemplate15.xaml
— шаблон для публикации результатов билда в NuGet и замены в файле AssemblyInfo.cs с дополнительными возможностями из версии TFSVersioning 1.5.
3. Настройка замен в файле AssemblyInfo.cs.
Если был выбран шаблон с участием TFSVersioning, в настройках билда появится
пункт № 4:
Что и для чего нужно:
- Первые две строки отвечают за шаблоны номеров версий для
AssemblyFileVersion
иAssemblyVersion
. - Правила замены для шаблона номеров версий:
- Номер, встречаемый в любом месте шаблона, остаётся неизменным.
B
заменяется на номер билда в рамках одного дня.YYYY
заменяются на 4-значное представление текущего года.YY
заменяются на 2-значное представление текущего года (две последние цифры).MM
илиM
заменяются на номер текущего месяца (при заменеMM
ноль впереди не ставится).DD
илиD
заменяются на номер текущего дня в месяце (при заменеDD
ноль впереди не ставится).J
заменяется на дату в форматеYYDDD
, гдеDDD
— порядковый номер дня с начала года.
Предлагаемый шаблон по умолчанию —
1.0.J.B
— обеспечивает практически уникальный номер версии для библиотеки. Сложности начнутся через 100 лет при условии сохранения мажорной и минорной версии проекта. Legacy-код наносит ответный удар. - Третья строка — это маска для поиска файлов AssemblyInfo в собираемых проектах. Обычно редактирования не требует.
- Четвёртая строка — это число, которое будет прибавлено к номеру
B
. Может применяться в случае настройки нескольких билдов для одного проекта — к одному прибавляем 100, к другому — 200, все счастливы. Максимальное значение для суммы номера билда и префикса — 65535. Автор проектов так же просит связаться с ним, если вы производите более 999 билдов в день, вы же явно чем-то интересным заняты. - Пятая строка указывает, нужно ли создавать аттрибуты
AssemblyFileVersion
иAssemblyVersion
при их отсутствии. - Шестая строка указывает, нужно ли заливать в хранилище данных изменённые файлы AssemblyInfo.
Если вы собираете более чем один проект, или вы по результатам билда создаёте ещё и NuGet-пакет, шаблоны для номеров версий можно вынести в отдельный XML-файл, залить его на сервер и включить его использование в седьмой строке, указав путь к нему в восьмой.
XML выглядит так:
<?xml version="1.0" encoding="utf-8" ?>
<VersionSeed>
<Solution name="Default">
<AssemblyVersionPattern>1.8.j.b</AssemblyVersionPattern>
<AssemblyFileVersionPattern>1.8.j.b</AssemblyFileVersionPattern>
</Solution>
<NuGetPackage id="ServiceLib">
<VersionPattern>1.8.j.b</VersionPattern>
</NuGetPackage>
</VersionSeed>
Если вы используете шаблон билда для TFSVersioning 1.5, вам так же будет доступна
вкладка с дополнительными возможностями автозамен в файле AssemblyInfo:
4. Настройка публикации в NuGet.
При использовании шаблона для NuGetter доступны ещё три вкладки.
NuGetter (A) – Pre-Packaging:
Первая из вкладок отвечает за подготовку собранного проекта к запаковке и публикации. При необходимости вы можете добавить на сервер свой PowerShell-скрипт (для создания папок, указанных в NuGet-спецификации, например).
Будьте внимательны: исполняемые PowerShell-скрипты могут быть запрещены к исполнению на сервере. В этом случае билд не выдаст ошибку, а просто попробует запустить NuGet так, как если бы скрипт отработал верно.
Политику относительно выполнения PowerShell-скриптов можно проверить и исправить через команды
Get-ExecutionPolicy
Set-ExecutionPolicy
NuGetter (B) – Package:
Что и для чего нужно:
- Первая строка — какие-либо дополнительные параметры для запуска NuGet-файла.
- Вторая строка — название базовой папки для выполнения команд NuGet при запаковке и публикации.
- Третья строка — путь к файлу NuGet на сервере (файл должен находиться в хранилище или в папках, определённых в PATH в Windows на сервере).
- Четвёртая строка — путь к файлу NuGet-спецификации на сервере (файл должен находиться в хранилище).
- Пятая строка — название папки, в которой будет создан пакет (если сборка пройдёт удачно).
- Шестая строка — адрес файла с шаблонами версий для проекта — как уже упоминалось выше, можно вынести в отдельный XML-файл все номера версий для проектов и пакетов, и не изменять их постоянно в свойствах билда.
У меня не получилось настроить рабочий процесс так, чтобы файл NuGet вызывал обновление самого себя перед выполнением — пришлось просто залить на сервер новую версию NuGet.exe. Думаю, нужно создавать свой шаблон на основе предоставленных, где добавлять Custom Action с ещё одним вызовом NuGet. Хотя, возможно, я изобретаю велосипед, и всё делается гораздо проще.
NuGetter ( C ) – Push and Publish:
Что и для чего нужно:
- Первая строка — API key, если он определён для NuGet-репозитория, в который идёт публикация пакета. Автор ожидает в качестве ключа GUID в записи
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
, если указанный ключ не проходит ReGex-проверку, строка считается относительным путём к файлу, в котором указан API key. - Вторая строка указывает, нужно ли просто создать пакет и скопировать его в репозиторий или требуется публикация на удалённый сервер. Фактически при включённом флаге добавляет параметр
-co
к параметрам запуска NuGet, чем рушит весь билд, так как в версии 2.1 параметр не распознаётся. - Третья строка указывает, нужно ли предпринимать попытку публикации пакета в репозиторий после его создания.
- Четвёртая строка — адрес NuGet-репозитория для публикации.
Возможные значения:- URL удалённого репозитория.
- UNC-имя папки в локальной сети.
- Локальный адрес папки на сервере, где запускается билд.
У нас используется стандартный NuGet Server, без каких-либо дополнений.
Для успешной публикации в NuGet-репозиторий обязательно нужны права на запись в папку packages для пользователя, под которым запущен Application Pool. Памяти на сервере тоже должно хватать, так как ошибка при нехватке места на диске в EventLog очень странная и абсолютно неинформативная.
5. Запуск билда.
Кроме автоматического запуска билда согласно установкам из пункта 1, билд можно запустить руками.
Team Explorer --> (Solution Name) --> Builds --> Билд для запуска --> правый клик --> Queue New Build ...
Таблица запущенных билдов с текущим статусом:
6. Итого.
Задача решилась, причём гораздо быстрее, чем была написана эта статья. Более того, после ознакомления с техникой создания билдов на TFS, оказалось, что настраивать Continious Integration не так уж и сложно. Важно помнить, что сборка билда происходит на сервере, поэтому настройки проекта, файлы и другие изменения нужно не забывать заливать в хранилище.
При неудачной сборке TFS автоматически создаёт баг, со срочностью Critical, и вешает его на разработчика, залившего код на сервер последним. После «починки» баг назначается на Network Service
, так что именно этому пользователю нужно выдавать права на запись.
Надеюсь, статья сэкономит кому-то время и нервы.
Спасибо за внимание.
Автор: VMAtm