Последовательности построений в TFS

в 13:04, , рубрики: .net, .net frameowrk, continuous integration, Team Foundation Server 2013, метки: , , ,

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

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

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

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

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

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

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

Последовательности построений в TFS

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

Последовательности построений в TFS

Для построения «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» типа массив строк.

Последовательности построений в TFS

В блоке «Выполение в агенте» создаём переменную «buildDetail» типа «IBuildDetail».
После блока «Try» добавляем «activity» «GetBuildDetail» и «QueueNewBuild».
В блоке «GetBuildDetail» в качестве результата «Result» задаём «buildDetail».
В блоке «QueueNewBuild» в качестве параметра «BuildDetail» задаём значение «buildDetail» полученное на предыдущем шаге и в качестве параметра «TfsProjectAndBuildDefinition» задаём значение «QueueNewBuilds».

Последовательности построений в TFS

Сохраняем изменения, компилируем и добавляем в 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».

Последовательности построений в TFS

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

Последовательности построений в TFS

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

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

Последовательности построений в TFS

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

Автор: Serg2DFX

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js