Мы в TeamCity всегда уделяли особое внимание .NET, его многочисленным инструментам и фреймворкам тестирования. В этом посте мы хотим рассказать о недавних обновлениях в нашей поддержке .NET и поделиться примером демо-проекта, который их иллюстрирует.


Сейчас поддержка .NET в TeamCity реализована с помощью огромного набора специализированных «ранеров» и «билд фичей». Ранеры обеспечивают интеграцию билда со сторонним софтом, а фичи выступают функциональными надстройками билда.


До версии 2020.1, TeamCity предоставлял следующие .NET компоненты:



Такое разнообразие компонентов позволяет TeamCity использовать весь потенциал .NET, но имеет и минусы. Например, чтобы правильно сгруппировать части проекта и выбрать ранеры, чтобы построить приложения и протестировать их на требуемых ОС, каждый раз приходится учитывать множество факторов:


  • Тип проекта
  • Целевая платформа
  • .NET-фреймворк
  • Тестовый фреймворк
  • Операционная система для тестирования

и т.д.


К счастью, с появлением .NET Core с открытым кодом и поддержкой кроссплатформенности, Microsoft постарались унифицировать и упорядочить инструменты разработки, объединив их в .NET SDK. Следующим шагом развития стал .NET 5 – он объединил .NET Core, .NET Framework, Xamarin и Mono. С версии 2020.1 TeamCity также объединил большинство своих компонентов, отвечающих за построение, тестирование и развертывание проектов, в один ранер .NET. Он консолидирует возможности ранеров из списка выше и предоставляет унифицированный подход. Мы надеемся, что это сильно упростит работу с .NET для наших пользователей. Новый ранер поддерживает:


  • Команды .NET CLI
  • Windows и кроссплатформенный MSBuild
  • «Честный» Visual Studio IDE (devenv)
  • Запуск Windows и кроссплатформенных тестов, в том числе NUnit и XUnit
  • Запуск Windows, .NET процессов и командных скриптов на разных операционных системах
  • Кроссплатформенную статистику покрытия кода
  • Docker

Мы будем ограниченно поддерживать устаревшие ранеры, чтобы обеспечить плавную миграцию проектов на единый ранер .NET, но дальнейшего развития они не получат. Рекомендуем учесть это при создании новых конфигураций и при переходе на .NET 5 из последних версии Visual Studio и Rider.


Структура демо-проекта


Технически, ранер .NET – результат глубокой переработки привычного пользователям раннера .NET CLI. В нём появилась масса новых возможностей, которые мы хотим показать на примере демонстрационного .NET-проекта. Его исходный код и скрипт конфигураций TeamCity находятся в этом репозитории. А уже развернутый проект TeamCity можно посмотреть на этом демо-сервере.


Демо-проект .NET состоит из нескольких .NET проектов:


Название


Тип проекта


Описание


Конфигурации TeamCity


Развертывание


Clock


.NET Standard 1.2


Библиотека общей логики


Создание пакета


NuGet публикация


NuGet


Clock.IoC


.NET Standard 1.2


Библиотека настройки IoC


Создание пакета


NuGet публикация


NuGet


Clock.Tests


.NET 5.0


Тесты общей логики


Тесты Windows


Тесты Linux


 


Clock.Console


.NET 5.0


Консольное приложение


Windows x64


Linux x64


Docker Nanoserver


Docker Ubuntu


Docker multi-arch


Docker


Clock.Web


.NET 5.0


Web приложение


Windows x64


Linux x64


Docker Nanoserver


Docker Ubuntu


Docker multi-arch


Docker


Clock.Desktop


.NET 4.8 WPF


Десктопное приложение


Создание дистрибутива


дистрибутив Windows


Clock.Desktop.Uwp


UAP


UWP-приложение


Создание пакета UWP


пакет UWP


Clock.Xamarin


.NET Standard 1.2


Общая библиотека представлений Xamarin


Cоздание Android-пакета


пакет Android


Clock.Xamarin.Android


Xamarin Android


Мобильное приложение Android


Cоздание Android-пакета


пакет Android



Для настройки CI/CD-процесса, мы используем иерархию из проектов и билд-конфигураций. Хотя TeamCity предоставляет дружественный пользовательский интерфейс, все проекты и конфигурации были созданы средствами TeamCity Kotlin DSL. Так удобнее версионировать настройки проектов, а затем делиться ими. При желании вы сможете использовать их на своем сервере, избежав ручной настройки. Более подробную информацию о том, как создавать конфигурации через код, используя DSL, можно найти здесь (на английском).


Для запуска конфигураций в туториале мы используем 2 типа агентов:


  • Windows 10 x64 Pro 10.0.19041
    • Visual Studio 2019 Pro 16.8.1
    • Docker (Windows container) 19.03.13
    • .NET SDK 5.0
  • Ubuntu 16.04
    • Docker 18.06.3

Первым шагом мы создали и разместили на GitHub стандартный проект на Maven, используя IntelliJ IDEA 2020.2.2 с поддержкой всех его возможностей: подсветкой кода, рефакторингом и т.д. Чтобы сделать поддержку проекта еще более удобной, в его DSL-настройках мы использовали наследование типов Kotlin.



Этот DSL-код нужно подключить к соответствующему проекту .NET в TeamCity. Если вы решите разместить проект у себя на сервере, создайте новый проект в TeamCity с VCS root’ом, указывающим на наш репозиторий с DSL-настройками. Затем, в секции Versioned Settings, включите синхронизацию настроек с системой версионирования и выберите формат настроек “Kotlin”:



После синхронизации настроек, TeamCity подгрузит DSL-код и создаст вложенные подпроекты и конфигурации из него.


В корневой проект .NET входят два подпроекта: «Building» и «Deployment». Они содержат соответствующие их названиям билд-конфигурации.



Помимо них, прямо в корневом проекте .NET лежат две общие билд-конфигурации – Build и Deploy. Первая собирает все приложения и пакеты из подпроектов:



вторая – разворачивает их, в данном случае в репозитории Docker и NuGet:



Все конфигурации сборки объединяет то, что каждая содержит набор системных параметров, передаваемых в .NET-команды в виде пар ключ-значение /p:key=value. Например, когда в билд-конфигурации определен системный параметр system.configuration=Release, то при запуске различных команд им передается параметр /p:configuration=Release. Префикс system. при этом пропадает, так как в TeamCity он указывает на тип параметра, а за его пределами будет лишним. Все приведенные ниже системные параметры, определенные в наших конфигурациях сборки, не специфичны для TeamCity и описаны в различной документации по .NET:


Параметр Значение в конфигурациях TeamCity Описание
Все конфигурации сборки configuration Release MSBuild-конфигурация.
VersionPrefix 1.0.0 Используется как базовая версия для приложений и пакетов.
VersionSuffix beta%build.number% Используется как пререлизная метка в версии для приложений и пакетов.
Build console and web InvariantGlobalization true Выполнять приложение без доступа к ресурсам, специфичным культуре.
Build Windows desktop PublishDir ../bin/Clock.Desktop/win/ Определяет путь публикации приложения.
AppxPackageDir ../bin/Clock.Desktop.Uwp/win/ Определяет путь публикации пакета UWP приложения.
Pack Copyright Copyright 2020 JetBrains Метаданные NuGet-пакетов.
Title TeamCity .NET sample
RepositoryType git
RepositoryUrl https://github.com/JetBrains/teamcity-dotnet-samples.git
RepositoryBranch refs/heads/master

Эти параметры можно включить непосредственно в проектные файлы или передавать через параметры командной строки. Лучше использовать системные параметры TeamCity, так как это позволяет не заботиться о деталях передачи специальных символов – TeamCity позаботится об этом сам. Рассмотрим конфигурации тестирования и сборки подробнее.


Конфигурации «Test on Windows и Test on Linux»


Две эти конфигурации тестируют общую логику приложений и собирают статистику покрытия кода в Windows агента #1 и в Linux Docker-контейнере mcr.microsoft.com/dotnet/core/sdk:5.0, используя один шаг сборки .NET. Для сценария Linux Docker, в UI он выглядит так:



В этом сценарии тесты и тестируемые библиотеки собираются и тестируются в Linux Docker-контейнере с .NET SDK 5.0. Конфигурация для тестов под Windows отличается лишь тем, что не использует Docker.


Тестовый проект Clock.Tests является приложением .NET 5.0, поэтому для построения и запуска тестов достаточно одной .NET Core CLI команды test. Для сбора и анализа статистики покрытия кода используется кроссплатформенный инструмент для оценки покрытия кода JetBrains dotCover из пакета JetBrains.dotCover.DotNetCliTool, который устанавливается как инструмент TeamCity. В DSL, тестовые конфигурации имеют общего предка TestBase и две конфигурации для тестов на Linux и для тестов на Windows.


Конфигурации «Build console and web for win-x64» и «Build console and web for linux-x64»


Эти две TeamCity конфигурации для Linux и для Windows собирают по два проекта Clock.Console и Clock.Web и состоят из двух шагов сборки, соответствующих этим проектам. Конфигурации наследуются от BuildConsoleAndWebBase, который в свою очередь наследуется от базового типа для всех конфигураций сборки приложений – BuildBase. Оба шага можно было бы объединить в один, указав в Projects сразу два проекта, но в этом случае было бы сложно разделить бинарные файлы от разных приложений, как это сейчас сделано через outputDir. Так как оба приложения имеют тип .NET 5.0, как и в предыдущем случае, для построения и публикации достаточно одной .NET Core CLI команды publish.


Вот как выглядит первый шаг в UI для Linux, который строит и публикует приложение Clock.Console в виде единственного запускаемого файла в папку, определенную в поле Output directory:



Для Windows, данный шаг отличается полем Runtime, в котором определено значение win-x64, и полем Output directory со значением bin/Clock.Console/win-x64.


Второй шаг для построения и публикации приложения Clock.Web для Linux отличается от первого шага, помимо пути к проектному файлу, полем Output directory со значением bin/Clock.Web/linux-x64. После завершения шагов, TeamCity публикует построенные приложения как артефакты билда.


Для win-x64:


  • bin/Clock.Console/win-x64/Clock.Console
  • bin/Clock.Console/win-x64/Clock.Web

Для linux-x64:


  • bin/Clock.Console/linux-x64/Clock.Console
  • bin/Clock.Console/linux-x64/Clock.Web

Конфигурация «Build Windows desktop»


В этой конфигурации всего один шаг:



На этом шаге запускается Windows MSBuild из Visual Studio 2019 на агенте #1 для выполнения «таргетов» Restore, Rebuild и Publish последовательно для каждого из двух проектов:


  • Clock.Desktop/Clock.Desktop.csproj
  • Clock.Desktop.Uwp/Clock.Desktop.csproj

Результаты сборок публикуются в разные директории, определенные в системных параметрах конфигураций PublishDir (для Clock.Desktop) и AppxPackageDir (для Clock.Desktop.Uwp). Эти директории далее публикуются как артефакты билда.


Конфигурация «Build Android app»


Эта конфигурация строит мобильное приложение Android, используя Windows MSBuild из Visual Studio 2019, на агенте #1:



Она аналогична предыдущей конфигурации, но вместо MSBuild «таргета» Publish здесь используется «таргет» SignAndroidPackage, чтобы опубликовать подписанный Android-пакет.


Конфигурация «Pack»


Эта конфигурация создает два NuGet-пакета и содержит один шаг .NET CLI с командой pack, выполненной последовательно для двух проектов – Clock и Clock.IoC:



Такие метаданные для NuGet-пакетов как название, тип репозитория и т.д., определены через системные параметры конфигурации TeamCity – их список есть в таблице выше. После успешного выполнения этой конфигурации, в артефактах появляются NuGet-пакеты, готовые для отправки в репозиторий на этапе развертывания.


Конфигурация «Build»


Эта особая конфигурация TeamCity не содержит шагов сборки. Она предназначена для того, чтобы собрать все артефакты приложений и пакетов в один TeamCity-билд через артефакт-зависимости на остальные конфигурации сборки:



Конфигурация «Deploy»


Это похожая конфигурация для развертывания, которая не содержит шагов, а только зависимости на другие конфигурации:



Чтобы подключить опубликованные NuGet-пакеты с общей логикой к проектам, можно использовать этот NuGet-источник. Для запуска приложений Clock.Console в Docker используйте команды:


docker pull nikolayp/clock-console
docker run -it --rm nikolayp/clock-console

А для Clock.Web:


docker pull nikolayp/clock-web
docker run -it --rm -p 5000:5000 nikolayp/clock-web

После запуска веб-приложения в контейнере, оно будет доступно по адресу http://localhost:5000/.


Все приложения и пакеты можно посмотреть в артефактах здесь.


Заключение


Microsoft активно развивает .NET 5, ведь он предоставляет разработчикам свободный и единый инструментарий. Мы в TeamCity верим, что наш обновленный ранер .NET позволит сделать непрерывную интеграцию проектов на .NET 5 удобной и предсказуемой.


Если вы уже пробовали новый ранер или просто хотите поделиться мнением о нем, пишите в комментариях. Всегда рады вашей обратной связи.