Думаю, я не единственный, кто считает, что у Microsoft есть плохие слова, когда дело доходит до изменения и/или расширения так называемого шаблона процесса сборки в TFS. Под катом рассказ о том, как мы перешли от Xaml к скриптам на F#/C#.
Как мы пытались интегрировать Фальшивый в TFS, но в итоге я получил свое решение — AnFake .
Статья будет полезна тем, кто использует TFS в качестве CI-решения, но не в восторге от его шаблонов процессов сборки.
В ходе разработки нашего проекта (который, по сути, представляет собой бинарный поставляемый модуль для, по сути, продукта компании) мы поняли, что хотим иметь CI-процесс, который был бы немного сложнее, чем просто компиляция и запуск модульных тестов.
.
Вот так выглядел в наших глазах «идеальный» процесс.
Обычная сборка:
- компиляция и запуск модульных тестов;
- подготовка тестовых образцов (образцы входят в комплект поставки и используются продуктом на последующих этапах тестирования; подготовка заключается в сборе файлов из разных мест, организации их в определенную структуру папок и упаковке);
- проведение тестов производительности; сохранение результатов (чтобы можно было строить графики); формирование предупреждения, если отклонение превышает нормативное;
- проведение тестов на ложные и истинные срабатывания; сохранение результатов для отчетности и/или дальнейших исследований.
- создание примечаний к выпуску об ошибках и задачах из TFS;
- публикация примечаний к выпуску на портале;
- автоматическая регистрация номера сборки в багах и задачах;
- выкладывание подготовленного архива поставки в определенное место для дальнейшего использования продукта.
Сборки CI в TFS указываются с использованием шаблона процесса (шаблона процесса сборки).
Первое, что мы попытались сделать для реализации нашего «идеала», — настроить шаблон процесса под себя и… столкнулись с рядом трудностей.
1. Часть логики сборки меняется по мере развития модуля (в частности, подготовки тестовых образцов), т.е.
желательно хранить эту логику в системе контроля версий вместе с другими исходниками модуля.
Но шаблон процесса в TFS 2012/2013 хранится отдельно и не версионируется (да, он есть в VCS, но всегда используется только последняя версия).
Почему это проблема? У нас есть как минимум две ветки: разработка и стабильная (теоретически еще могут быть релизные ветки, если нужно делать хотфиксы) и как минимум некоторые шаги сборки в них различаются, т.е.
мы не можем собрать все ветки по одному шаблону.
Возможные решения:
- Создайте версию шаблона вручную, добавив номер версии к имени файла шаблона.
При этом при объединении веток нельзя забывать об объединении шаблонов.
А если вдруг помимо редактирования шаблона вам понадобятся свои активности, то псевдоверсионность придется применять и к dll с этими активностями!
- Поместите изменяющуюся логику в скрипт и вызовите этот скрипт из шаблона.
Скрипт следует хранить вместе с остальными исходниками модуля.
На первом этапе - обычный bat файл.
2. Использование скрипта привело нас к еще одной проблеме — резко упала информативность логирования.
Сообщения просто пишутся в консоль, у них нет уровней, и отличить предупреждение от диагностики невозможно.
Вы можете выделить только ошибку, написав сообщение в stderr. Но каждая строка, прочитанная TFS из stderr, будет восприниматься им как отдельная ошибка.
Таким образом, например, форматированное трехстрочное сообщение об ошибке в TFS будет отображаться как три отдельные ошибки.
«Это некрасиво», но жить можно.
3. Скрипт начал разрастаться.
В дополнение к этапу подготовки образца вскоре он включал в себя тесты производительности и тесты FP/TP. Почему это? Казалось бы, запуск тестов должен быть в шаблоне.
Дело в том, что эти тесты проводятся инструментами, которые также являются частью нашего модуля и соответственно развиваются вместе с ним, поэтому меняется и способ запуска.
Оставив их в шаблоне, мы имеем уже рассмотренную в пункте 1 проблему.
Более того.
Стало созревать желание иметь доступ из скрипта к свойствам текущей сборки (название ветки, номер версии и т.д.), а также общаться с другими сборками для организации конвейера.
На этом этапе стало понятно, что скрипт должен быть мощнее, чем просто батник.
А учитывая, что возиться с шаблонами процессов не так уж и удобно, возникло желание перенести всю логику в скрипт, а шаблон использовать только как оболочку для загрузки исходников и передачи управления скрипту.
Кандидатами на сценарий были: PSake , Грабли И Фальшивый .
После быстрого анализа PSake был удален из-за синтаксиса PowerShell; Рейк — т.к.
никто в нашей команде не был знаком с Руби; и остался Фейком.
Справедливости ради надо сказать, что F# на тот момент в команде тоже никто не знал, но F# поддерживается Visual Studio из коробки, поэтому мы остановились на нем.
К сожалению, Fake не дружит с TFS. А что, мы не программисты что ли?! Мы сами облажались.
Можно сказать, что проблемы 1 и 3 решены.
Однако проблема 2 усугубилась тем, что.
теперь вся сборка выполнялась в скрипте, результат, показанный в Visual Studio, выглядел удручающе - никаких предупреждений не было видно (в том числе и от компилятора); какие этапы были выполнены не видно; ошибки показаны как жуткие портянки.
Мы попытались решить эту проблему, написав расширение для Fake, но у Fake не было единой точки журналирования, из которой можно было бы перенаправить структурированный вывод в TFS. Попутно возникло еще несколько недовольств (подробнее можно прочитать о здесь ).
Тем не менее, мне понравилась идея использовать полноценный язык программирования для описания сборки.
В итоге я решил в свободное время реализовать идею Фейка в собственном исполнении.
Акцент был сделан на:
- расширяемость (например, подключаемый html-отчет о сборке или публикация результатов тестирования в базу данных и т. д.);
- интеграция с различными внешними системами (первой на очереди была TFS);
- читаемый и понятный журнал и вывод ошибок.
В результате получился очень достойный инструмент (я назвал его AnFake = Another F# Make), который может пригодиться всем, кто «воюет» с TFS. Давайте посмотрим, как он выглядит и что он умеет (на данный момент AnFake в основном предназначен для TFS, поэтому дальнейшее обсуждение будет в контексте TFS).
Пусть у нас есть решение под названием Demo, которое находится в системе контроля версий TFS:
Давайте также создадим рабочую область «Demo.dev», настроенную с одним сопоставлением:$/TeamProject/Demo /dev /Demo.App /Demo.Lib /Demo.Lib.Test Demo.sln
$/TeamProject/Demo/dev: C:\Projects\Demo.dev
(далее предполагается, что используется схема «одно рабочее пространство на филиал») Откройте C:\Projects\Demo.dev\Demo.sln в Visual Studio. Установите AnFake как пакет NuGet:
PM> Install-Package AnFake
При установке пакета в корневой папке решения будет создано несколько файлов:
- build.fsx — базовый скрипт сборки, включающий вызов MSBuild и запуск тестов;
- anf.cmd — псевдоним для вызова AnFake (чтобы не писать каждый раз .
/packages/AnFake.x.y.z/bin/AnFake.exe);
- .
workspace — текстовый файл с описанием отображений из рабочей области, внутри которой было загружено текущее решение;
- .
nuget\NuGet.config — [создается, только если он не существует] содержит параметр DisableSourceControlIntegration, позволяющий предотвратить передачу двоичных файлов пакета в VCS.
Tfs.PlugIn()
let out = ~~".
out"
let productOut = out / "product"
let testsOut = out / "tests"
let tests = !!"*/*.
Test.csproj"
let product = !!"*/*.
csproj" - tests
"Clean" => (fun _ ->
let obj = !!!"*/obj"
let bin = !!!"*/bin"
Folders.Clean obj
Folders.Clean bin
Folders.Clean out
)
"Compile" => (fun _ ->
MsBuild.BuildRelease(product, productOut)
MsBuild.BuildRelease(tests, testsOut)
)
"Test.Unit" => (fun _ ->
VsTest.Run(testsOut % "*.
Test.dll")
)
"Test" <== ["Test.Unit"]
"Build" <== ["Compile"; "Test"]
.
то же самое в C# Tfs.PlugIn();
var outDir = ".
out".
AsPath(); var productOut = out / "product"; var testsOut = out / "tests"; var tests = "*/*.
Test.csproj".
AsFileSet(); var product = "*/*.
csproj".
AsFileSet() - tests; "Clean".
AsTarget().
Do(() => { var obj = "*/obj".
AsFolderSet(); var bin = "*/bin".
AsFolderSet(); Folders.Clean(obj); Folders.Clean(bin); Folders.Clean(out); }); "Compile".
AsTarget().
Do(() => { MsBuild.BuildRelease(product, productOut); MsBuild.BuildRelease(tests, testsOut); }); "Test.Unit".
AsTarget().
Do(() => { VsTest.Run(testsOut % "*.
Test.dll"); }); "Test".
AsTarget().
DependsOn("Test.Unit"); "Build".
AsTarget().
DependsOn("Compile", "Test");
В принципе, этого уже достаточно для запуска локальной сборки: PM> .
\anf Build
(здесь мы использовали консоль диспетчера пакетов, но AnFake можно запустить из любой консоли командной строки)
В результате получаем примерно такой отчет:
Мы видим, что компиляция прошла с одним предупреждением; Было выполнено 2 теста, один из которых был пропущен.
Хорошо, фиксируем изменения.
Коммит будет содержать файл .
nuget/packages.config (здесь NuGet записывает пакеты уровня решения) и файлы, созданные во время установки AnFake, в корневой папке решения.
Теперь запустим ту же сборку через TFS. Для этого вам необходимо установить специальный шаблон AnFakeTemplate.xaml (делается только один раз для командного проекта): PM> .
\anf "[AnFakeExtras]/vs-setup.fsx" "BuiltTemplate" -p "TeamProject"
где вместо TeamProject, естественно, нужно подставить имя вашего проекта в TFS.
Команда создаст временное рабочее пространство с именем AnFake.BuildTemplate.yyyymmdd.hhmmss; загрузится во временную папку $/TeamProject/BuildProcessTemplates; добавит шаблон AnFakeTemplate.xaml и несколько сопутствующих библиотек.
Команда НЕ ДЕЛАЕТ ничего автоматически, чтобы не вызвать бурю оправданного негодования.
Поэтому заходим в Visual Studio -> Team Explorer -> Pending Changes, переключаемся в рабочую область AnFake.BuildTemplate, просматриваем изменения (убедимся, что там нет ничего лишнего) и коммитируем.
Теперь мы можем создать определение сборки:
- Перейдите в Visual Studio -> Team Explorer -> Сборки, выберите «Новое определение сборки».
- На вкладке Процесс в разделе Шаблоны процессов сборки нажмите Показать подробности.
- Нажмите «Создать» и в поле «Путь управления версиями» введите (или выберите через Source Control Explorer) $/TeamProject/BuildProcessTemplates/AnFakeTemplate.v2.xaml; Нажмите «ОК» (делается только один раз для командного проекта).
- В раскрывающемся списке Файл процесса сборки выберите AnFakeTemplate.v2.xaml и сохраните.
В результате получаем следующий отчет:
AnFakeTemplate выполняет три простых шага:
- Загружает решение из системы контроля версий.
- Восстанавливает пакеты NuGet уровня решения (т. е.
загружает сам AnFake).
- Передает управление AnFake.
- Все остальное определяется в сценарии.
Таким образом, вы можете обновить пакет AnFake без необходимости обновлять шаблон.
У вас даже могут быть разные версии AnFake в разных решениях и они будут безопасно собраны по одному шаблону.
Я продемонстрировал базовый сценарий интеграции AnFake в TFS. Однако интеграция не ограничивается шаблоном: к свойствам текущей сборки можно обратиться из скрипта; получить доступ к артефактам из других сборок; есть даже возможность организовать конвейер.
Кроме того, AnFake обеспечивает дополнительную автоматизацию при работе с маппингами и рабочими пространствами: маппинги могут храниться в VCS вместе с остальными исходниками проекта, рабочее пространство будет создаваться и обновляться автоматически.
Не хочу перегружать статью описанием всех этих вкусностей здесь и сейчас, но если такой подход будет интересен и инструмент будет востребован, то в ближайшее время напишу продолжение.
В заключение хотелось бы сказать, что «идеальная» сборка, описанная в начале статьи, на данный момент полностью реализована и работает на AnFake. Я не могу показать наши реальные сценарии, но вы можете увидеть некоторые интересные отрывки.
Здесь .
Буду признателен за ваш отзыв.
Спасибо.
Теги: #автоматизация сборки #непрерывная интеграция #tfs #C++ #F# #.
NET #C++ #F#
-
Винительный Дизайн
19 Oct, 24 -
Синтетический Мониторинг Производительности
19 Oct, 24 -
World Machine + Ue4: Полный Рабочий Процесс
19 Oct, 24 -
Нарисуем Карту Креативного Класса Москвы
19 Oct, 24 -
Отчет О Конференции Loc Kit
19 Oct, 24