Последовательности построений в 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 сервер (данные пункты думаю не имеет смысла расписывать, т.к. они подробно расписаны например тут: www.ewaldhofman.nl/post/2010/05/27/Customize-Team-Build-2010-e28093-Part-7-How-is-the-custom-assembly-found.aspx ).

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

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

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

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

Код доступен на Git: github.com/Serg2DFX/QueueNewBuilds/.

ссылка на оригинал статьи http://habrahabr.ru/post/215369/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *