Дано
- Одна маленькая, но очень полезная библиотека. Включает в себя общую функциональность — логирование, работа с 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...
![](https://habrastorage.org/storage2/9a4/fb6/bd6/9a4fb6bd68bc5ba37b1bc898b6c25c2b.png)
Выбор контроллера:
![](https://habrastorage.org/storage2/074/ccd/e8c/074ccde8c0373740a5765398d09e089f.png)
Редактирование контроллера для (Solution Name):
![](https://habrastorage.org/storage2/be4/cff/dc8/be4cffdc8e083d24e088f4010687b151.png)
В поле
Version control path to custom assemblies
требуется указать путь к вашей папке с общими библиотеками.Здесь у меня возникла первая маленькая проблема — я пытался редактировать свойства не контроллера, а одного из его агентов сборки. Будьте внимательны.
На всякий случай проверьте подключение к папке (кнопка
Test Connection
).После проверки подключения сохраните изменения.
1. Добавление нового билда на основе шаблона.
Теперь нужно добавить новый билд по шаблону, который был загружен в предыдущем шаге. Делается это так:
Team Explorer --> (Solution Name) --> Builds --> правый клик --> New Build Definition...
![](https://habrastorage.org/storage2/3f6/039/d30/3f6039d3095e87e1830247d83bfe79ca.png)
Название и описание билда:
![](https://habrastorage.org/storage2/694/0b5/044/6940b5044a0d213660bd9b84545dcf86.png)
Расписание для запуска билда:
![](https://habrastorage.org/storage2/dac/968/828/dac968828b623c61305eee83fb9be581.png)
Варианты:
- Запуск только вручную.
- После каждого check-in от разработчиков.
- Ждать завершения предыдущего билда.
- Собирая check-in, с возможностью поставить время запуска «не чаще чем N минут».
- Принимать check-in только если был удачный merge и сборка на сервере после этого была успешной.
- По расписанию.
Код, который должен быть собран во время билда:
![](https://habrastorage.org/storage2/86a/433/c88/86a433c8889839b663b0ec99ab34bca0.png)
Иногда здесь присутствует слишком много проектов — удалите лишние. Так же можно указать какой проект в какую папку должен быть скопирован во время билда.
Определение параметров по умолчанию для билда:
![](https://habrastorage.org/storage2/a21/956/46d/a2195646db63353823f857f740908966.png)
Необходимо выбрать:
- Контроллер, который был настроен в предыдущем пункте.
- Куда нужно поместить код:
- Не копировать результат сборки никуда.
- Скопировать результат сборки в общую папку
Требуется корректный UNC-адрес вида\\server\share
. - Скопировать результат сборки в папку TFS-сервера
Вариант не всегда доступен, в зависимости от прав пользователя, создающего билд.
Настройки билда:
![](https://habrastorage.org/storage2/84f/eae/ab4/84feaeab488a1b67c56141a6337622d2.png)
Первые три пункта — это настройки, доступные всегда и для любого билда. Сейчас для нас представляет интерес только первый блок — выбор проектов или решений для сборки (
Projects to Build
).Так же стоит обратить внимание на
Build number format
— именно этот параметр отвечает за имя папки с результатом билда (для проектов с большой вложенностью папок должен быть не очень длинным).Настройки сохранения результатов билдов:
![](https://habrastorage.org/storage2/807/671/e7c/807671e7ce06082702fd891e778de411.png)
2. Выбор шаблона для билда.
На вкладке
Process
в верхней части в выпадающем списке нужно выбрать требуемый шаблон. Если в выпадающем списке не видно нужного, значит, он будет использоваться в первый раз, и его необходимо «показать» серверу с помощью кнопки New...
(выбрать или скопировать уже загруженный на TFS-сервер файл). Выбор шаблона:
![](https://habrastorage.org/storage2/091/d61/913/091d619132aaf5cd0fb80207042d4f35.png)
Варианты:
- [Все установленные на 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:
![](https://habrastorage.org/storage2/a19/2e7/984/a192e79849f2107476bab514e3b196d4.png)
Что и для чего нужно:
- Первые две строки отвечают за шаблоны номеров версий для
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:
![](https://habrastorage.org/storage2/fa2/b96/faf/fa2b96fafce4134e487516623dec90bc.png)
4. Настройка публикации в NuGet.
При использовании шаблона для NuGetter доступны ещё три вкладки.
NuGetter (A) – Pre-Packaging:
![](https://habrastorage.org/storage2/620/11c/d9f/62011cd9fbef59744baf999d194ccc30.png)
Первая из вкладок отвечает за подготовку собранного проекта к запаковке и публикации. При необходимости вы можете добавить на сервер свой PowerShell-скрипт (для создания папок, указанных в NuGet-спецификации, например).
Будьте внимательны: исполняемые PowerShell-скрипты могут быть запрещены к исполнению на сервере. В этом случае билд не выдаст ошибку, а просто попробует запустить NuGet так, как если бы скрипт отработал верно.
Политику относительно выполнения PowerShell-скриптов можно проверить и исправить через команды
Get-ExecutionPolicy
Set-ExecutionPolicy
NuGetter (B) – Package:
![](https://habrastorage.org/storage2/dd5/688/af6/dd5688af6522ea39732de0a3577178fa.png)
Что и для чего нужно:
- Первая строка — какие-либо дополнительные параметры для запуска NuGet-файла.
- Вторая строка — название базовой папки для выполнения команд NuGet при запаковке и публикации.
- Третья строка — путь к файлу NuGet на сервере (файл должен находиться в хранилище или в папках, определённых в PATH в Windows на сервере).
- Четвёртая строка — путь к файлу NuGet-спецификации на сервере (файл должен находиться в хранилище).
- Пятая строка — название папки, в которой будет создан пакет (если сборка пройдёт удачно).
- Шестая строка — адрес файла с шаблонами версий для проекта — как уже упоминалось выше, можно вынести в отдельный XML-файл все номера версий для проектов и пакетов, и не изменять их постоянно в свойствах билда.
У меня не получилось настроить рабочий процесс так, чтобы файл NuGet вызывал обновление самого себя перед выполнением — пришлось просто залить на сервер новую версию NuGet.exe. Думаю, нужно создавать свой шаблон на основе предоставленных, где добавлять Custom Action с ещё одним вызовом NuGet. Хотя, возможно, я изобретаю велосипед, и всё делается гораздо проще.
NuGetter ( C ) – Push and Publish:
![](https://habrastorage.org/storage2/0ae/582/aa1/0ae582aa1b54b0e33d43eb25fedbdc7d.png)
Что и для чего нужно:
- Первая строка — API key, если он определён для NuGet-репозитория, в который идёт публикация пакета. Автор ожидает в качестве ключа GUID в записи
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
, если указанный ключ не проходит ReGex-проверку, строка считается относительным путём к файлу, в котором указан API key. - Вторая строка указывает, нужно ли просто создать пакет и скопировать его в репозиторий или требуется публикация на удалённый сервер. Фактически при включённом флаге добавляет параметр
-co
к параметрам запуска NuGet, чем рушит весь билд, так как в версии 2.1 параметр не распознаётся. - Третья строка указывает, нужно ли предпринимать попытку публикации пакета в репозиторий после его создания.
- Четвёртая строка — адрес NuGet-репозитория для публикации.
Возможные значения:
- URL удалённого репозитория.
- UNC-имя папки в локальной сети.
- Локальный адрес папки на сервере, где запускается билд.
Для успешной публикации в NuGet-репозиторий обязательно нужны права на запись в папку packages для пользователя, под которым запущен Application Pool. Памяти на сервере тоже должно хватать, так как ошибка при нехватке места на диске в EventLog очень странная и абсолютно неинформативная.
5. Запуск билда.
Кроме автоматического запуска билда согласно установкам из пункта 1, билд можно запустить руками.
Team Explorer --> (Solution Name) --> Builds --> Билд для запуска --> правый клик --> Queue New Build ...
![](https://habrastorage.org/storage2/4d8/b31/5ef/4d8b315ef777990cf993362cd010b380.png)
Таблица запущенных билдов с текущим статусом:
![](https://habrastorage.org/storage2/dc8/8bc/a7f/dc88bca7fb1dbf7ab7fc74c735e3302e.png)
6. Итого.
Задача решилась, причём гораздо быстрее, чем была написана эта статья. Более того, после ознакомления с техникой создания билдов на TFS, оказалось, что настраивать Continious Integration не так уж и сложно. Важно помнить, что сборка билда происходит на сервере, поэтому настройки проекта, файлы и другие изменения нужно не забывать заливать в хранилище.
При неудачной сборке TFS автоматически создаёт баг, со срочностью Critical, и вешает его на разработчика, залившего код на сервер последним. После «починки» баг назначается на
Network Service
, так что именно этому пользователю нужно выдавать права на запись.Надеюсь, статья сэкономит кому-то время и нервы.
Спасибо за внимание.