Batch copying build definitions in TFS 2010

[get the source code of this sample utility]

This post is part my “merging team projects series” but can be considered independently. I’ll explain and publish a code template that will help you to batch copy build definitions from a team project to another. This is all possible thanks to the TFS API.

More than just raw copying

I’m not the first to blog about this, and you can find various small pieces of code here, or here. Why am I bothering then ? Because what I intend to do is more than just raw copying, we need also to transpose Workspaces, change build templates location, edit build process parameters, edit build templates in the source controller and check them in…

Features:

  • Regex selection of build names on command line
    • I recommend using a batch file with every possible filters in there are many
  • 3 log files in append mode (support for batch file with many calls)
  • Workspace transformations (customizable in the code)
    • Easy to report paths that are non-standard according to your own rules
  • Build process parameters transformations (customizable in the code)
  • Copy, transform and check-in files in the source controller (customizable in the code)

Limitations:

  • Single TFS Server (cannot migrate to another server)
  • Need to customize the C# code to get exactly what you want

Prerequisistes

My starting point what Jim Lamb’s piece of code.

You’ll need to reference a few classic TFS assemblies, including a private one: Microsoft.TeamFoundation.Build.Workflow.dll, you’ll find it in C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies. Note that this reference will force you to change the targeted framework to .NET Framework 4.0 instead of the client profile, but that doesn’t matter too much for that kind of utility.

image

I’ve also included an easy to use class for processing command line parameters C#/.NET Command Line Arguments Parser, many thanks to GriffonRL.

I wrote a small utility class to deal with workspaces, you’ll find it in this project.

Code Highlights

A few pieces of code that can be of interest:

// clone the build into the target team project
IBuildDefinition newDefinition = Utilities.CloneBuildDefinition(_bs, buildDef, targetTeamProject);

As I mentioned earlier, this basically calls Jim Lamb’s piece of code in order to get the build object properly duplicated.

// accessing the build process parameters stored in TFS in a serialized format
IDictionary<String, Object> processParameters = WorkflowHelpers.DeserializeProcessParameters(buildDef.ProcessParameters);

Continue reading

[TFS API] A tiny class to help with TFS Workspace creation and cleanup

I made a small helper class to make workspace programming with the TFS API a bit more straightforward. When you want to manipulate files in the TFS version control, you *have* to use a Workspace, nothing can exists or can be touched outside of a Workspace. Well, not exactly, you can always use the DownloadFile method, but this is a one file at a time, read only access you’ll get. Actually, I almost never need this, I keep using Workspaces. I have to admit I get bored at it because using workspaces means declaration, mappings and local file manipulations: no way you can work directly on the server. That is why having a few helpers can be handful.

So this is a temporary Workspace creation and auto-cleanup class. The cleanup occurs in the Dispose method so that you can use it with the using keyword without having to worry about it.

The typical use case I’m aiming is:

  • Connect to TFS
  • Create the temp workspace inside a using statement
  • Map a few folders (a Map method is provided)
  • Do some operations, because you get access to the real Workspace object
  • Don’t bother with the cleanup it is done when you leave the using block

Enough talk:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;

namespace WorkspaceDemo
{
    /// <summary>
    /// Tiny helper class for using temporary workspaces.
    /// </summary>
    /// <see cref="http://www.vlaquest.com"/>
    public class TfsTempWorkspace : IDisposable
    {
        private Workspace _workspace;
        private VersionControlServer _vcs;

        /// <summary>
        /// Creates a workspace. If a workspace with the same name exists, it will be deleted and recreated!
        /// </summary>
        public TfsTempWorkspace(VersionControlServer vcs, string workspaceName, string userName)
        {
            _vcs = vcs;
            _workspace = _vcs.QueryWorkspaces(workspaceName, userName, Environment.MachineName).FirstOrDefault();

            if (_workspace != null)
            {
                _workspace.Delete();
            }
            _workspace = _vcs.CreateWorkspace(workspaceName);
            cleanDirectory();
        }

        /// <summary>
        /// Give access to many properties of the workspace
        /// </summary>
        public Workspace Workspace
        {
            get
            {
                return _workspace;
            }
        }

        /// <summary>
        /// Adds a mapping to the workspace.
        /// The local folders and files will be created under the user TEMP directory.
        /// </summary>
        /// <param name="serverPath">Full path on server, starting from root, ex: $/MyTP/MyFolder</param>
        /// <param name="localRelativePath">A relative path inside the local workspace folder structure</param>
        /// <returns>The local full path</returns>
        public string Map(string serverPath, string localRelativePath)
        {
            string localPath = Path.Combine(Path.GetTempPath(), _workspace.Name, localRelativePath);
            _workspace.Map(serverPath, localPath);
            return localPath;
        }

        public void cleanDirectory()
        {
            string rootPath = Path.Combine(Path.GetTempPath(), _workspace.Name);
            if (Directory.Exists(rootPath))
            {
                DeleteDirectory(rootPath);
            }
        }

        /// <summary>
        /// All local files are deleted, and the workspace is then removed from the server
        /// </summary>
        public void Dispose()
        {
            cleanDirectory();
            _workspace.Delete();
        }

        public static void DeleteDirectory(string target_dir)
        {
            string[] files = Directory.GetFiles(target_dir);
            string[] dirs = Directory.GetDirectories(target_dir);

            foreach (string file in files)
            {
                File.SetAttributes(file, FileAttributes.Normal);
                File.Delete(file);
            }

            foreach (string dir in dirs)
            {
                DeleteDirectory(dir);
            }

            Directory.Delete(target_dir, false);
        }
    }
}

And a working example:
Continue reading