Pull to refresh

Автоматическая публикация новой версии библиотеки с использованием TFS 2010 и NuGetter

Reading time8 min
Views5.6K

Дано


  1. Одна маленькая, но очень полезная библиотека. Включает в себя общую функциональность — логирование, работа с Windows Azure, и т.д.
  2. Большое количество проектов(solutions), где используется данная библиотека.
  3. Распределённая команда разработчиков, часть которой библиотеку пишет и поддерживает, а другая часть только пользуется.

Проблемы, которые хочется решить

  1. Необходимость копировать из проекта в проект исходники/бинарники — неудобно, долго, велика вероятность ошибки при обновлении.
  2. Невозможность использования разных версий для разных проектов — поиск и сборка конкретной версии «из прошлого» неудобны, опять же велика вероятность ошибки при обновлении.
  3. Необходимость следить за актуальностью зависимостей библиотеки — особенно это касается Azure SDK, который сейчас регулярно обновляется, не всегда у всех разработчиков стоит последняя версия, и обновление SDK не всегда возможно.
  4. Использование существующего проекта на разных машинах — ещё одно «тонкое» место, порождающее много ненужных ошибок. Для корректной работы необходимо полное совпадение путей для проектов, чего очень сложно добиться.

Способ решения и возникшие проблемы

Сразу стало понятно, что получение пакета с последней/конкретной версией библиотеки при сборке какого-либо решения проще всего сделать через 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-адрес вида \\server\share.
    • Скопировать результат сборки в папку 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, так что именно этому пользователю нужно выдавать права на запись.

Надеюсь, статья сэкономит кому-то время и нервы.
Спасибо за внимание.
Tags:
Hubs:
+3
Comments22

Articles

Change theme settings