[Перевод] Интеграция Cake и TeamCity

Cake — замечательный инструмент для создания конвеера доставки для ваших приложений. Я люблю его, поскольку он позволяет мне писать этот конвеер на языке C#, который я знаю хорошо. Прекрасным свойством Cake, PSake и дургих подобных фреймворков является то, что они создают скрипт, который можно выполнять как на локальной машине разработчика, так и на CI серверах. Здесь я объясню, как организовать взаимодействие скрипта Cake с TeamCity.


Требования

Я буду полагать, что вы уже имеете начальные знания по Cake и TeamCity. В противном случае можете начать с чтения следующих ресурсов:

Для Cake:

Для TeamCity:

Теперь давайте поговорим о взаимодействии Cake и TeamCity.


Логирование

Конвеер Cake обычно состоит из нескольких задач (task). Было бы здорово иметь для каждой такой задачи отдельную секцию в журнале (build log) TeamCity. Я бы хотел получить сворачиваемую секцию для каждой задачи Cake в журнале:

Журнал

Cake API имеет методы TeamCity.WriteStartBuildBlock и TeamCity.WriteEndBuildBlock. Хотя и возможно использовать их в каждой задаче, но это можно автоматизировать. В Cake есть методы TaskSetup и TaskTeardown, которые вызываются перед и после каждой задачи. Они могут открывать и закрывать блоки журнала TeamCity:

TaskSetup(setupContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteStartBuildBlock(setupContext.Task.Name);
   }
});

TaskTeardown(teardownContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteEndBuildBlock(teardownContext.Task.Name);
   }
});

Здесь свойство TeamCity.IsRunningOnTeamCity используется чтобы определить, выплняется код на TeamCity или нет.

Теперь в нашем журнале есть сворачиваемые блоки для каждой задачи. Но можно добавить еще одно улучшение.

Обычно задачи в Cake имеют короткие имена: Build, Test, Clean. Так их легче запускать из командной строки. Но в журнале TeamCity я бы предпочел иметь более развернутые описания задач Cake. И это возможно сделать. Чтобы дать задаче описание, используйте метод Description:

Task("Clean")
.Description("Create and clean folders with results")
.Does(() => { ... });

Теперь эти описания можно использовать для формирования блоков в журнале:

TaskSetup(setupContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteStartBuildBlock(setupContext.Task.Description ?? setupContext.Task.Name);
   }
});

TaskTeardown(teardownContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteEndProgress(teardownContext.Task.Description ?? teardownContext.Task.Name);
   }
});

Это позволяет повысить его читаемость.


Прогресс выполнения

Если работа скрипта Cake занимает много времени, полезно знать, какая именно задача выполняется в данный момент.

Индикация прогресса

Этого можно достичь с помощью методов TeamCity.WriteStartProgress и TeamCity.WriteEndProgress. Их вызовы можно вставить в те же TaskSetup и TaskTeardown:

TaskSetup(setupContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteStartBuildBlock(setupContext.Task.Description ?? setupContext.Task.Name);

      TeamCity.WriteStartProgress(setupContext.Task.Description ?? setupContext.Task.Name);
   }
});

TaskTeardown(teardownContext =>
{
   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.WriteEndProgress(teardownContext.Task.Description ?? teardownContext.Task.Name);

      TeamCity.WriteEndBuildBlock(teardownContext.Task.Description ?? teardownContext.Task.Name);
   }
});


Результаты тестов

Если вы выполняете тесты в задаче Cake, то TeamCity способна показать вам их результаты.

vwo5trt8i07uu0ug2znbt5gdfw0.png

Это можно сделать с помощью метода TeamCity.ImportData. Он принимает два параметра: строковое описание типа данных и путь к файлу, эти данные содержащему. Например, есть вы используете MSTest, вот как сообщить TeamCity о результатах выполнения тестов:

Task("Run-Tests")
.Description("Run tests")
.IsDependentOn("Clean")
.IsDependentOn("Build")
.Does(() => {
   var testDllsPattern = string.Format("./**/bin/{0}/*.*Tests.dll", configuration);

   var testDlls = GetFiles(testDllsPattern);

   var testResultsFile = System.IO.Path.Combine(temporaryFolder, "testResults.trx");

   MSTest(testDlls, new MSTestSettings() {
      ResultsFile = testResultsFile
   });

   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.ImportData("mstest", testResultsFile);
   }
});

TeamCity поддерживает несколько типов тестов. Кроме mstest вы можете использовать nunit, vstest и некоторые другие.


Анализ покрытия кода тестами

TeamCity способен показать результат анализа покрытия кода тестами.

xncpksl_kk8_bee4vslmj8fxvem.png

На данный момент TeamCity поддерживает интеграцию с DotCover. Позвольте показать, как использовать DotCover в скрипте Cake. Сначала DotCover нужно установить:

#tool "nuget:?package=JetBrains.dotCover.CommandLineTools"

После этого его можно использовать в задачах:

Task("Analyse-Test-Coverage")
.Description("Analyse code coverage by tests")
.IsDependentOn("Clean")
.IsDependentOn("Build")
.Does(() => {
   var coverageResultFile = System.IO.Path.Combine(temporaryFolder, "coverageResult.dcvr");

   var testDllsPattern = string.Format("./**/bin/{0}/*.*Tests.dll", configuration);

   var testDlls = GetFiles(testDllsPattern);

   var testResultsFile = System.IO.Path.Combine(temporaryFolder, "testResults.trx");

   DotCoverCover(tool => {
         tool.MSTest(testDlls, new MSTestSettings() {
            ResultsFile = testResultsFile
         });
      },
      new FilePath(coverageResultFile),
      new DotCoverCoverSettings()
         .WithFilter("+:Application")
         .WithFilter("-:Application.*Tests")
      );

   if(TeamCity.IsRunningOnTeamCity)
   {
      TeamCity.ImportData("mstest", testResultsFile);

      TeamCity.ImportDotCoverCoverage(coverageResultFile);
   }
});

Как видите, здесь так же прогоняются и тесты. Поэтому мы сразу можем сообщить TeamCity о результатах тестов и результатах анализа покрытия ими кода. Метод TeamCity.ImportDotCoverCoverage именно и делает это последнее.


Публикация артефактов

В TeamCity вы можете опубликовать некоторые артефакты, созданные в процессе работы скрипта Cake. Хорошим кандидатом на эту роль являются NuGet пакеты:

hrb3vyetri0inx9dyhqrqepbqvg.png

Чтобы сделать это, положите все артефакты, которые хотите опубликовать, в одну папку. Затем можно выполнить публикацию методом TeamCity.PublishArtifacts:

Task("Publish-Artifacts-On-TeamCity")
.Description("Publish artifacts on TeamCity")
.IsDependentOn("Create-NuGet-Package")
.WithCriteria(TeamCity.IsRunningOnTeamCity)
.Does(() => {
   TeamCity.PublishArtifacts(artifactsFolder);
});


Заключение

Надеюсь, эти простые примеры кода сэкономят вам время и усилия, если вы хотите, чтобы ваш скрипт Cake работал на TeamCity. Полную версию скрипта и обрабатываемого им приложения можно найти на GitHub. Удачи!

© Habrahabr.ru