共用方式為


TFS 2010 – Managing Build Process Templates (what are those?)

If you haven’t noticed already in 2010. New build definitions no longer use the tfsBuild.proj file or the Microsoft.TeamFoundation.Build.targets file. With the switch to Windows Workflow as the base technology that the build process runs on we no longer need those MSBuild artifacts.

But we do need a way for you to define the build process. For that, we have XAML files checked into Version Control (example $/teamproject/BuildProcessTemplates/DefaultTemplate.xaml) that tell the build machine how to build whatever it is you want to build. These XAML files don’t have to live in this folder, like the 2008 TfsBuild.proj file, you can have them live anywhere. But the definition has to know where they live, so you can choose one to run your build. To make that user experience really simple, the build server has a list of the registered build process templates.

New Team Projects start off with 2 build process templates (3 if you count the lab template): the Default template and the Upgrade template. You may have noticed that when you create a new definition, it automatically chooses the DefaultTemplate for you. Even if you use the add button and create a template called A_template (alphabetically before defaulttemplate), a new build definition will still choose the default template.

But there’s another way to create a definition, you can checkin a TfsBuild.proj file into a new folder under the TeamBuildTypes folder like you would do in 2008. This will create a new build definition for you. However, this definition will use the upgrade template!

So, how does the server know which template to use? Well, when the XAML files are registered with the server, you can actually specify a type of Default, Upgrade, or Custom. Any templates you create will be defined as custom, but during team project creation, we create the default and upgrade templates for you and mark them with the appropriate type. Simple, right. But what if you want to change the default to point to one of your custom templates.

Unfortunately, there is no way to accomplish that without writing some code. Here is a sample console application that I created for this purpose. It allows you to see the list of registered templates, add your own, and even change which ones are the default and upgrade templates (there can be only one default and one upgrade template per team project).

 using System;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;

namespace ManageBuildTemplates
{
    class Program
    {
        /// <summary>
        /// args[0] - collection url
        /// args[1] - team project name
        /// args[2] - command (list, setDefault, setUpgrade, add, addDefault, addUpgrade, remove)
        /// args[3] - template server path (used for set,add,remove commands)
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            try
            {

                if (args.Length < 3)
                {
                    PrintUsage();
                    return;
                }

                TfsTeamProjectCollection collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(args[0]));
                IBuildServer buildServer = collection.GetService<IBuildServer>();

                switch (args[2].ToLower())
                {
                    case "add":
                        AddTemplate(buildServer, args[1], args[3], ProcessTemplateType.Custom);
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "adddefault":
                        AddTemplate(buildServer, args[1], args[3], ProcessTemplateType.Default);
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "addupgrade":
                        AddTemplate(buildServer, args[1], args[3], ProcessTemplateType.Upgrade);
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "list":
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "remove":
                        RemoveTemplate(buildServer, args[1], args[3]);
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "setdefault":
                        SetTemplateType(buildServer, args[1], args[3], ProcessTemplateType.Default);
                        ListTemplates(buildServer, args[1]);
                        break;

                    case "setupgrade":
                        SetTemplateType(buildServer, args[1], args[3], ProcessTemplateType.Upgrade);
                        ListTemplates(buildServer, args[1]);
                        break;

                    default:
                        PrintUsage();
                        return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine("An error occured:");
                Console.WriteLine(ex.Message);
                Console.WriteLine();
            }
        }

        static void ListTemplates(IBuildServer buildServer, String teamProjectName)
        {
            Console.WriteLine();
            Console.WriteLine("Build Process Templates for Team Project: {0}", teamProjectName);
            Console.WriteLine();
            Console.WriteLine("Type            Filename");
            Console.WriteLine("--------------- --------------------------------------------");

            IProcessTemplate[] templates = buildServer.QueryProcessTemplates(teamProjectName);
            foreach (IProcessTemplate pt in templates)
            {
                Console.WriteLine("{0,15} {1}", pt.TemplateType.ToString(), pt.ServerPath);
            }

            Console.WriteLine();
        }

        static void SetTemplateType(IBuildServer buildServer, String teamProjectName, String serverPath, ProcessTemplateType newType)
        {
            Console.WriteLine();
            Console.WriteLine("Changing the type of Build Process Template");
            Console.WriteLine("    Team project: {0}", teamProjectName);
            Console.WriteLine("    Server path:  {0}", serverPath);
            Console.WriteLine();

            IProcessTemplate[] templates = buildServer.QueryProcessTemplates(teamProjectName);

            if (newType == ProcessTemplateType.Default || newType == ProcessTemplateType.Upgrade)
            {
                // Make sure there isn't already a template with that type
                // - there can be only one upgrade or default template for a team project
                foreach (IProcessTemplate pt in templates)
                {
                    if (pt.TemplateType == newType)
                    {
                        Console.WriteLine("Existing template found with the type '{0}'.", newType);
                        Console.WriteLine("    Server path: {0}", pt.ServerPath);
                        Console.WriteLine("    Changing type for this template to custom.");
                        Console.WriteLine();
                        pt.TemplateType = ProcessTemplateType.Custom;
                        pt.Save();
                    }
                }

            }

            foreach (IProcessTemplate pt in templates)
            {
                if (pt.ServerPath.Equals(serverPath, StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine("Template found.");
                    Console.WriteLine("    Changing type for the template to '{0}'.", newType);
                    Console.WriteLine();
                    pt.TemplateType = newType;
                    pt.Save();
                    return;
                }
            }

            Console.WriteLine("Template NOT found.");
            Console.WriteLine();
        }

        static void AddTemplate(IBuildServer buildServer, String teamProjectName, String serverPath, ProcessTemplateType newType)
        {
            Console.WriteLine();
            Console.WriteLine("Adding the {0} Build Process Template", newType.ToString());
            Console.WriteLine("    Team project: {0}", teamProjectName);
            Console.WriteLine("    Server path:  {0}", serverPath);
            Console.WriteLine();

            IProcessTemplate template = buildServer.CreateProcessTemplate(teamProjectName, serverPath);
            template.Save();
            Console.WriteLine("Template added (as a custom build template).");
            Console.WriteLine();

            if (newType == ProcessTemplateType.Default || newType == ProcessTemplateType.Upgrade)
            {
                SetTemplateType(buildServer, teamProjectName, serverPath, newType);
            }
        }

        static void RemoveTemplate(IBuildServer buildServer, String teamProjectName, String serverPath)
        {
            Console.WriteLine();
            Console.WriteLine("Removing the Build Process Template");
            Console.WriteLine("    Team project: {0}", teamProjectName);
            Console.WriteLine("    Server path:  {0}", serverPath);
            Console.WriteLine();

            IProcessTemplate[] templates = buildServer.QueryProcessTemplates(teamProjectName);
            foreach (IProcessTemplate pt in templates)
            {
                if (pt.ServerPath.Equals(serverPath, StringComparison.OrdinalIgnoreCase))
                {
                    ProcessTemplateType type = pt.TemplateType;
                    pt.Delete();
                    Console.WriteLine("Template found and removed.");
                    if (type != ProcessTemplateType.Custom)
                    {
                        Console.WriteLine("Note: You have removed the '{0}' template for this team project.", type);
                    }
                    Console.WriteLine();

                    return;
                }
            }

            Console.WriteLine("Template not found.");
            Console.WriteLine();
        }

        static void PrintUsage()
        {
            Console.WriteLine("Usage:");
            Console.WriteLine("ManageBuildTemplates <tfsCollectionUrl> <teamProjectName> add|addDefault|addUpgrade|list|remove|setDefault|setUpgrade [templateServerPath]");
        }
    }
}

The writelines and comments should give you enough information on how to use the app, but just in case here are some examples:

1) Add my template for a XAML file I already checked in:

>ManageBuildTemplates.exe https://jpricket-test:8080/tfs/TestCollection0 TestProject add  $/TestProject/BuildProcessTemplates/MyTemplate.xaml

2) Change the default template to be my template:

>ManageBuildTemplates.exe https://jpricket-test:8080/tfs/TestCollection0 TestProject setDefault $/TestProject/BuildProcessTemplates/MyTemplate.xaml

I hope you enjoy it!

Comments

  • Anonymous
    September 23, 2010
    The comment has been removed

  • Anonymous
    November 17, 2010
    Hi, Anyone found solution for the error "   TF42073:The value cannot be null(parameter name:process)" I am facing this issue too... Thanks in advance Peeyush Sodhia sodhia17@gmail.com

  • Anonymous
    February 17, 2011
    I did exactly what jason pointed in this blog and it worked.

  • Anonymous
    April 05, 2011
    For a team project that is created with 2008 based custom template, the build process template is not added in the source control.  After I add by myself to source control and when I tried to create new build definition I am getting error as " you do not have permission to create new buld definition". And then I tried to use the above program to register the maually added 'default' and 'upgrade' templates to the 'team project' so that I can start creating build definition.  But that also fails by givign permission error. How can I resolve this issue with out recreating the project with new process template? Thanks iamPriya@gmail.com

  • Anonymous
    April 06, 2011
    Hi Jason,   After setting up the permission on the BUILD folder, the above code worked for me!   Great work!

  • Anonymous
    November 08, 2011
    I tried to view this post in IE and Firefox but both browsers seem to cut off lines of code in the post so I can not use the code. Do you have a link to downloadable version?

  • Anonymous
    December 08, 2011
    The comment has been removed

  • Anonymous
    September 06, 2012
    The comment has been removed

  • Anonymous
    September 06, 2012
    Hi Dushan, It's hard to say what the problem is without seeing the error. My guess would be that the Version Control Security settings have been changed for the BuildProcessTemplates folder or for the entire team project folder. You can get to VC security by opening Source Control Explorer and right clicking on the appropriate folder to bring up properties. You may also want to ask your admin exactly what they did :) Thanks, Jason