...

вторник, 11 марта 2014 г.

[Из песочницы] Последовательности построений в TFS

При использовании подхода непрерывной интеграции часто возникает потребность в запуске не просто одного построения, а в запуске последовательности построений.

Это актуально когда код продукта разделён на несколько проектов, и они зависят друг от друга.

В данной статье я хочу рассказать, как это можно сделать, используя TFS сервер.


Пусть у нас есть два проект «Assembly1» и «Assembly2».

Для проекта «Assembly1» настроено два построения: «ClassLibrary1» и «ClassLibrary2».

Для проекта «Assembly2» тоже настроено два построения: «A2.t2» и «A2.t3».


Нам требуется после запуска построения «ClassLibrary1» запустить последовательно «ClassLibrary2» и «A2.t2».


1. Простой и медленный способ (мы так делали раньше).


Настраиваем построения «ClassLibrary2» и «A2.t2» специальным образом:


Указываем построение при check-in.



Добавляем рабочие папки от предыдущего контроллера построения.



Для построения «A2.t2» выполняются аналогичные действия.


Преимущество здесь одно – не требуется модифицировать используемый рабочий процесс для построения.

А вот недостатков больше:

— Чем больше используется «рабочих каталогов», тем дольше осуществляется построение.

— Последовательность построений невозможно запустить вручную, только check-in.

— Последовательность построений может выполниться в произвольном порядке, что не всегда хорошо.

В нашем случае мы не знаем, какое построение выполнится раньше «ClassLibrary2» или «A2.t2».


2. Используем TFS API.


— Создаём новый проект «ClassLibrary».

— Добавляем новый элемент «CodeActivity».

— Создаём два входных параметра «BuildDetail» и «TfsProjectAndBuildDefinition» первый используется для получения и управления BuildServer'ом.

— Разбираем список проектов и построений.

— Данный список состоит из строк, где каждая строка определяет имя проекта, для которого осуществляется построение и имя построения разделённых точкой с запятой.

Ниже приведён код:



using System;
using System.Activities;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using Microsoft.TeamFoundation.Build.Workflow.Services;
using Microsoft.TeamFoundation.Client;

namespace QueueNewBuilds
{
[BuildActivity(HostEnvironmentOption.Agent)]
public sealed class QueueNewBuild : CodeActivity
{
// The Team Project that the build definition belongs to.
[RequiredArgument]
public InArgument<IBuildDetail> BuildDetail { get; set; }

[RequiredArgument]
public InArgument<String[]> TfsProjectAndBuildDefinition { get; set; }


protected override void Execute(CodeActivityContext context)
{
String[] dirty = context.GetValue(this.TfsProjectAndBuildDefinition);
IBuildDetail buildDetail = context.GetValue(this.BuildDetail);

var pds = Parse(dirty);
//var workspace = buildDetail.BuildDefinition.Workspace;
IBuildServer buildServer = buildDetail.BuildServer;

foreach (var pd in pds)
{
try
{
string message = string.Format("Queue new build \"{0}\"-\"{1}\"", pd.TfsProject, pd.BuildDefinition);
context.TrackBuildMessage(message);

IBuildDefinition buildDef = buildServer.GetBuildDefinition(pd.TfsProject, pd.BuildDefinition);
buildServer.QueueBuild(buildDef);
}
catch (Exception ex)
{
string message = string.Format("Queue new build error \"{0}\"-\"{1}\", Exception : \"{2}\"",
pd.TfsProject, pd.BuildDefinition, ex.Message);
context.TrackBuildWarning(message);
}
}
}

private IEnumerable<ProjectDefinition> Parse(string[] dirty)
{
if (dirty == null)
yield break;

foreach (var item in dirty)
{
var t = item.Split(';');
if (t.Length == 2)
{
ProjectDefinition pd = new ProjectDefinition();
pd.TfsProject = t[0].Trim();
pd.BuildDefinition = t[1].Trim();
yield return pd;
}
}
}

class ProjectDefinition
{
public string TfsProject { get; set; }
public string BuildDefinition { get; set; }
}
}
}


Для использования данной «activity» делаем копию «TfvcTemplate.12.xaml»

Модифицируем процесс построения:

Создаём новый аргумент «QueueNewBuild» типа массив строк.



В блоке «Выполение в агенте» создаём переменную «buildDetail» типа «IBuildDetail».

После блока «Try» добавляем «activity» «GetBuildDetail» и «QueueNewBuild».

В блоке «GetBuildDetail» в качестве результата «Result» задаём «buildDetail».

В блоке «QueueNewBuild» в качестве параметра «BuildDetail» задаём значение «buildDetail» полученное на предыдущем шаге и в качестве параметра «TfsProjectAndBuildDefinition» задаём значение «QueueNewBuilds».



Сохраняем изменения, компилируем и добавляем в TFS сервер (данные пункты думаю не имеет смысла расписывать, т.к. они подробно расписаны например тут: http://ift.tt/1foea6S ).


Для построения «ClassLibrary1» выполняем настройку.

Выбираем модифицированный процесс. У меня он называется «TfvcTemplate.12.2.xaml».



Задаём последовательность построения.



Сохраняем, запускаем построение и наслаждаемся последовательно выполняющимся построением.


Результаты можно обнаружить в лог файле.



Код доступен на Git: http://ift.tt/1foea72.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий