Статьи

Пользовательская стратегия управления версиями в TFS 2010 для Windows Phone

Прочтите это руководство, в котором показано, как реализовать формат major.minor.build.revision в Team Foundation Server при разработке приложений для Windows Phone.

Недавно я начал создавать множество приложений для Windows Phone 7 и решил сделать следующий шаг и начать внедрять автоматизацию сборки для проектов Windows Phone 7. Я боролся с одной версией. Я хотел иметь возможность использовать формат major.minor.build.revision, и я хотел, чтобы это использовалось на ярлыках, а также на именах папок расположения.

Мне очень трудно поверить, что почти нет блогов на эту конкретную тему пользовательских версий .; хотя есть несколько блогов, которые делают это, они все еще не упоминают важную часть использования BuildNumber для создания DropLocation и установки LabelName с использованием buildNumber, который был моей главной мотивацией для этого блога. Кажется, что каждая организация, с которой я работаю, чтобы помочь в миграции или внедрении TFS 2008 и TFS 2010, всегда задает вопрос об использовании пользовательских версий.

Пользовательское управление версиями — это не что иное, как возможность управления версиями в формате {major}. {Minor}. {Build}. {Revision} . Проблема с TFS 2008 и TFS 2010 заключается в том, что по умолчанию он использует $ (Date: yyyyMMdd) $ (Rev: .r), который почти никто не хочет использовать по крайней мере из моего опыта работы с моими клиентами.

В TFS 2008 было достаточно просто переопределить BuildNumberOverrideTarget с помощью пользовательской задачи MSBuild. Но в TFS 2010, если вы решите использовать подход Workflow, это не так очевидно и требует немного больше работы. Мне потребовалось время, чтобы придумать что-то, что было бы хорошим решением.

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

Итак, вот ваши ожидаемые окончательные результаты.

1) Пользовательский номер сборки с использованием major.minor.build.revision.

2) Установите LabelName, используя пользовательскую версию.

3) Создать DropLocation, используя пользовательскую версию.

4) Windows Phone 7 Xap файл.

Вроде бы много, но для всего этого будет повод. В следующих разделах вы научитесь: 1) настраивать номер сборки с помощью настраиваемой активности рабочего процесса, 2) интегрировать его в рабочий процесс и 3) создавать 7-битные Windows Phone.

Подготовка сервера сборки

Обязательно прочтите мой блог на эту тему, если вы планируете настроить сервер сборки для Windows Phone 7 .

Предполагает

Я сделаю следующее, чтобы сделать этот блог более простым.

1) По крайней мере, созданное ранее определение сборки.

2) Имеет общее представление о TFS 2010 и Workflow или просматривал следующие полезные блоги, которые мне нравится читать:

Блог Джима Лэмба на TFS 2010

Мартин Вудворд TFS 2010 связанные блоги

Учебные пособия Эвальда Хофмана по TFS 2010 Workflow

Создание пользовательских действий

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

Проверять, выписываться

Я не помню, откуда я взял этот код, но я внес некоторые изменения, добавив шаблоны поиска, чтобы позволить извлекать файлы, соответствующие определенному шаблону поиска.

using System.Activities;

using System.IO;

using Microsoft.TeamFoundation.Build.Client;

using Microsoft.TeamFoundation.VersionControl.Client;

 

namespace TfsBuildActivitiesLib.Activities

{

    [BuildActivity(HostEnvironmentOption.Agent)]

    public sealed class Checkout : CodeActivity

    {

        // The file mask of all files for which the buildnumber of the

        // AssemblyVersion must be increased

        [RequiredArgument]

        public InArgument<string> SearchPatterns { get; set; }

 

        // The workspace that is used by the build

        [RequiredArgument]

        public InArgument<Workspace> Workspace { get; set; }

 

        protected override void Execute(CodeActivityContext context)

        {

            // Obtain the runtime value of the input arguments

            string[] searchPatterns = context.GetValue(this.SearchPatterns).Split(‘;’);

 

            Workspace workspace = context.GetValue(this.Workspace);

 

            // Checks all files out in the workspace that apply to the file mask

            // For every workspace folder (mapping)

            foreach (var folder in workspace.Folders)

            {

                foreach (var searchPattern in searchPatterns)

                {

                    // Get the files that apply to the mask on the local system

                    foreach (var file in Directory.GetFiles(folder.LocalItem,

                                                    searchPattern, SearchOption.AllDirectories))

                    {

                        // Check all those file out

                        workspace.PendEdit(file);

                    }

                }

            }

        }

    }

}

 

SetCustomVersion

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

Обычно я помещаю файл version.txt в корень папки, где находится файл решения, где я хочу, чтобы этот продукт имел конкретную версию, и отслеживаю эту версию, используя version.txt, как показано ниже.

Для манипулирования version.txt и получения новой версии пользовательской активности вы будете использовать OutArgument с именем NewVersion, как показано в приведенном ниже коде. NewVersion будет доступен для рабочего процесса, и вы будете использовать его для изменения BuildNumber и DropLocation, найденных в BuildDetail, с помощью действия SetBuildPropties TFS.

using System;

using System.Activities;

using System.IO;

using Microsoft.TeamFoundation.Build.Client;

 

namespace TfsBuildActivitiesLib.Activities

{

    [BuildActivity(HostEnvironmentOption.Agent)]

    public sealed class SetCustomVersion : CodeActivity

    {

 

        [RequiredArgument]

        public InArgument<IBuildDetail> BuildDetail { get; set; }

 

        [RequiredArgument]

        public InArgument<string> VersionText { get; set; } // i.e. wp7\duckcaller\version.txt

 

        [RequiredArgument]

        public InArgument<string> SourceDir { get; set; } // i.e. wp7\duckcaller\version.txt

 

        public OutArgument<string> NewVersion { get; set; } // i.e. wp7\duckcaller\version.txt

 

        protected override void Execute(CodeActivityContext context)

        {

            var srcDir = SourceDir.Get(context);

            var versionText = VersionText.Get(context);

 

            var versionFile = Path.Combine(srcDir, versionText);

 

            if (File.Exists(versionFile))

            {

                Version version =  new Version(File.ReadAllText(versionFile));

 

                Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, 0);

                var buildDetail = BuildDetail.Get(context);

 

                File.WriteAllText(versionFile, newVersion.ToString());

 

                this.NewVersion.Set(context, newVersion.ToString());

            }

            else

            {

                throw new ArgumentException(versionFile + “Does not exist!”);

            }

        }

 

    }

}

 

SetAssemblyVersion

Это пользовательское действие будет выполнять поиск в DirectoryList (содержит разделенный точкой с запятой список каталогов относительно исходного каталога) и помечает файлы AssemblyInfo.cs версией, как показано в приведенном ниже коде.

using System.Activities;

using Microsoft.TeamFoundation.Build.Client;

using Microsoft.TeamFoundation.Build.Workflow.Activities;

using System.ComponentModel;

using System.IO;

using System.Text.RegularExpressions;

 

namespace TfsBuildActivitiesLib.Activities

{

    [BuildActivity(HostEnvironmentOption.All)]

    [DisplayName("Set Assembly Version Task")]

    public sealed class SetAssemblyVersion : CodeActivity

    {

        [RequiredArgument]

        public InArgument<string> BuildNumber { get; set; }

        [RequiredArgument]

        public InArgument<string> DirectoryList { get; set; } // ; delimited (i.e root\dir3;c:\root\dir4)

        [RequiredArgument]

        public InArgument<string> SearchPatterns { get; set; } // ; delimited (i.e assemblyinfo.*;assembly.cs)

        [RequiredArgument]

        public InArgument<string> SourcesDirector { get; set; }

 

        protected override void Execute(CodeActivityContext context)

        {

            // Build Number

            string buildNumber = context.GetValue<string>(BuildNumber);

            context.TrackBuildMessage(“Preparing For Setting Assembly Version: “ + buildNumber, BuildMessageImportance.High);

 

            string sourceDir = SourcesDirector.Get(context);

 

            // Get directories to search

            string dirList = DirectoryList.Get(context);

            string[] directories = string.IsNullOrEmpty(dirList) ? null : dirList.Split(‘;’);

            context.TrackBuildMessage(“Searching Directories: “ + dirList, BuildMessageImportance.High);

 

            // Get Assembly File Names

            string assemblyInfoFileMasks = SearchPatterns.Get(context);

            if (!string.IsNullOrEmpty(assemblyInfoFileMasks))

            {

                context.TrackBuildMessage(“Setting Assemblies: “ + assemblyInfoFileMasks, BuildMessageImportance.High);

                foreach (string dir in directories)

                {

                    DirectoryInfo dirInfo = new DirectoryInfo(Path.Combine(sourceDir, dir));

                    foreach (string assemblyInfoFileMask in assemblyInfoFileMasks.Split(‘;’))

                    {

                        foreach (FileInfo file in dirInfo.GetFiles(assemblyInfoFileMask, SearchOption.AllDirectories))

                        {

                            context.TrackBuildMessage(string.Format(“Setting version on {0}”, file.FullName), BuildMessageImportance.High);

                            ChangeAssemblyVersion(file, buildNumber);

                        }

                    }

                }

            }

        }

 

        private void ChangeAssemblyVersion(FileInfo assemblyFile, string buildNumber)

        {

            string contents = string.Empty;

 

            using (StreamReader reader = assemblyFile.OpenText())

            {

                contents = reader.ReadToEnd();

                reader.Close();

            }

 

            string newAssemblyVersion;

            string newAssemblyFileVersion;

 

            if (assemblyFile.Extension.ToLower().Equals(“.cs”))

            {

                // c#

                newAssemblyVersion = “[assembly: AssemblyVersion(\"" + buildNumber + "\")]“;

                newAssemblyFileVersion = “[assembly: AssemblyFileVersion(\"" + buildNumber + "\")]“;

 

                contents = Regex.Replace(contents, @”\[assembly: AssemblyVersion\("".*""\)\]“, newAssemblyVersion);

                contents = Regex.Replace(contents, @”\[assembly: AssemblyFileVersion\("".*""\)\]“, newAssemblyFileVersion);

            }

            else

            {

                // vb

                newAssemblyVersion = “<Assembly: AssemblyVersion(\”" + buildNumber + “\”)>”;

                newAssemblyFileVersion = “<Assembly: AssemblyFileVersion(\”" + buildNumber + “\”)>”;

 

                contents = Regex.Replace(contents, @”\<Assembly: AssemblyVersion\(“”.*”"\)\>”, newAssemblyVersion);

                contents = Regex.Replace(contents, @”\<Assembly: AssemblyFileVersion\(“”.*”"\)\>”, newAssemblyFileVersion);

            }

 

            using (StreamWriter writer = new StreamWriter(assemblyFile.FullName, false))

            {

                writer.Write(contents);

                writer.Close();

            }

        }

    }

}

 

Регистрироваться

Я также получил этот код откуда-то, я не помню, откуда я их взял, поэтому, пожалуйста, напишите комментарий ниже, чтобы отдать должное этому коду. Эта задача проверит все файлы, которые извлекаются в текущей рабочей области, и код показан ниже. Не забывайте оставлять комментарий «*** NO_CI ***», если вы делаете непрерывную интеграцию.

using System.Activities;

using Microsoft.TeamFoundation.Build.Client;

using Microsoft.TeamFoundation.VersionControl.Client;

using Microsoft.TeamFoundation.Build.Workflow.Activities;

 

namespace TfsBuildActivitiesLib.Activities

{

    [BuildActivity(HostEnvironmentOption.Agent)]

    public sealed class Checkin : CodeActivity

    {

        // The workspace that is used by the build

        [RequiredArgument]

        public InArgument<Workspace> Workspace { get; set; }

 

        protected override void Execute(CodeActivityContext context)

        {

            // Obtain the runtime value of the input arguments

            Workspace workspace = context.GetValue(this.Workspace);

 

            context.TrackBuildMessage(workspace.Name, BuildMessageImportance.High);

 

            // Checks all files in in the workspace that have pending changes

            // The ***NO_CI*** comment ensures that the CI build is not triggered (and that

            // you end in an endless loop)

            workspace.CheckIn(workspace.GetPendingChanges(), “Build Agent”, “***NO_CI***”,

                null, null, new PolicyOverrideInfo(“Auto checkin”, null),

                CheckinOptions.SuppressEvent);

        }

 

    }

}

 

Использование BuildNumber для установки DropLocation

Есть блоги, которые покажут вам, как настроить сборку, но не с конкретным случаем использования major, minor, build и revision. И самое главное, как убедиться, что DropLocation использует BuildNumber.

Если вы посмотрите на шаблон сборки по умолчанию, создание DropLocation происходит после задачи «Обновить номер сборки», как показано на рисунке ниже.

Здесь проблема. Вы должны прочитать version.txt (какой бы механизм вы не использовали для чтения предыдущей версии, чтобы вы могли увеличить номер версии), прежде чем вы сможете установить действия Drop Location и Create Drop Location, как показано выше.

Чтобы преодолеть эту проблему, вам нужно сделать две вещи.

1. Сначала переместите три действия: «Установить местоположение отбрасывания», «Создать местоположение отбрасывания» и, если «Сбросить сборку» и «Причина сборки» действительны, для действий «Выбор версии» в последовательности пользовательских версий, показанной на самом первом рисунке, показанном вверху.

2. После того как вы переместили фигуры вниз, вам нужно изменить Set Drop Location и Set Drop Location Private, чтобы включить BuildNumber, как показано на диаграмме ниже.

Затем установите BuildNumber с помощью NewVersion, который является результатом действия SetAssemblyVersion, и измените DropLocation на следующее.

BuildDetail.DropLocationRoot + «\» + BuildDetail.BuildDefinition.Name + «\» + NewVersion

Определите аргументы рабочего процесса и создайте пользовательские свойства сборки и определение сборки

В приведенных выше пользовательских действиях, которые вы закодировали, вы заметите, что публичные свойства имеют атрибуты RequiredArgument. Предполагается, что эти свойства будут передаваться в настраиваемые действия из рабочего процесса, и вы должны определить аргументы, а затем создать метаданные параметров процесса, чтобы их можно было задавать при создании определения сборки. Ниже приведен процесс создания аргументов для передачи в действия и создания метаданных для передачи из определения сборки.

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

Конечный результат

Итак, вот окончательный результат, как показано на рисунке ниже.

1) Пользовательский номер сборки с использованием major.minor.build.revision.

2) Установите LabelName, используя пользовательскую версию.

3) Создать DropLocation, используя пользовательскую версию.

4) Windows Phone 7 Xap файл. если вы правильно подготовили сервер сборки Windows Phone 7, вы увидите файл Xap в DropLocation при создании решения, содержащего проект Windows Phone 7.

Скачать код

TFS XAML

Вывод

В этом блоге вы создали проект Windows Phone 7 в TFS 2010 с использованием Workflow. В рамках процесса, который вы научились создавать пользовательскую версию (major.minor.build.revision), установите пользовательскую версию в качестве метки и используйте пользовательскую версию для создания места размещения.

Источник: http://blog.toetapz.com/2010/12/02/custom-versioning-strategy-on-tfs-2010-using-workflow-for-windows-phone-7-application/