Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

TFS Rev-Up, A Utility to Increment Build Revisions

0.00/5 (No votes)
25 Aug 2013 1  
A utility to increment build numbers for TFS

Introduction

My team uses TFS (Microsoft Team Foundation Server 2010) to do nightly builds. With TFS, it is very easy to set up nightly builds. However, TFS does not auto-increment the build number or contain any mechanism to do this. This means that every build has the same number, unless a developer changes it. This project is a utility that will increment the build number, every time it is run.

The process for incrementing the build number is:

  1. Check-out your AssemblyInfo.cs/.vb file
  2. Increment the lowest digit (1.1.1.1 becomes 1.1.1.2)
  3. Check-in the file

My team scheduled this utility to run each night before the scheduled build. However, we could have chosen to include it into the build template or workflow. If we ever switch to a “Continuous Integration” process, we will move this utility into the build template (or workflow).

Prerequisites

  1. To run this, you need to have the TFS client files installed.
  2. You need permissions to TFS and any TFS path that you will change/increment. If you are running this project (EXE) as a scheduled process, then the scheduler (or task) needs to use an account with those permissions.

Setting Things Up

Project

I created a command line project (with an optional GUI). To do this, I created a “Visual C#“, Windows, “Windows Forms Application”. After the project was created, I went into the Project Properties, under the Application tab, I changed the Output type to “Console Application”. Finally, I changed the Main() function to detect command-line mode versus UI mode, by testing for command-line arguments:

    static void Main(string[] args)
    {
        //if no command line args then start with UI
        if (args == null || args.Length < 1)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMain());
        }
        else //run in silent mode
        {
            if (args[0].Contains("silent"))
                Console.WriteLine("Version Revved up to " + TfsRevUp.DoRevUp(
                   TfsAutoRevUp.Properties.Settings.Default.TfsServerUrl, 
                   TfsAutoRevUp.Properties.Settings.Default.AssemblyInfoFilePath));
            else
                Console.WriteLine("Use the /silent arg to run in silent mode.\n" + 
                   "Example: TfsRevUp.exe /silent");
        }
    }

Reference the TFS Assemblies

In the C# project, the following DLLs (TFS assemblies) are needed:

  • Microsoft.TeamFoundation.dll
  • Microsoft.TeamFoundation.Client.dll
  • Microsoft.TeamFoundation.Common.dll
  • Microsoft.TeamFoundation.VersionControl.Client.dll
  • Microsoft.TeamFoundation.VersionControl.Common.dll

These files can usually be found in the folder: c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\

Make a Configuration Setting that Points to TFS

I set up this utility to work for one project at a time. The settings went into the app.config file (TfsRevUp.exe.config) so I can change it without recompiling.

Using the Code

All of the work for this utility is in one class, named TfsRevUp(.cs).

namespace TfsRevUp
{
    class TfsRevUp
    {
       public static string DoRevUp(string TfsServerUrl, string AssemblyInfoFilePath)
       {

The values for these two arguments will come from the app.config file (Project Settings, above)

Includes

To accomplish my goals, I need to use two different TFS libraries, at the same time. One is for the TFS server and one is for the TFS client. The names are long, so I used an alias for the client namespace:

using TFS=Microsoft.TeamFoundation.VersionControl.Client;

Screen shot (UI or command line)

- or -

Connect to TFS

TFS is designed to host several services. Source control is only one of them. So I need two connections:

Microsoft.TeamFoundation.Client.TeamFoundationServer tfServer;
TFS.VersionControlServer verServer;
 
//connect to the TFS server
tfServer = new Microsoft.TeamFoundation.Client.TeamFoundationServer(TfsServerUrl);
 
//connect to the Source Control service
verServer = tfServer.GetService<TFS.VersionControlServer>();
 

Create a Workspace

//TFS needs a workspace to do any check-in/out stuff 
const string cWorkspaceName = "RevUp-TempWorkspace";
TFS.Workspace[] workspaces = verServer.QueryWorkspaces
(cWorkspaceName, verServer.AuthorizedUser, System.Environment.MachineName);
TFS.Workspace wsp;
 
if (workspaces == null || workspaces.Length < 1)
   wsp = verServer.CreateWorkspace(cWorkspaceName, verServer.AuthorizedUser);
else
   wsp = workspaces[0];

Map the Workspace to a Local HDD Path

string strLocalPath = System.IO.Directory.GetCurrentDirectory() + "\\Temp\\";
 
//if the local path (temp) doesn’t exist, create it now
if (!System.IO.Directory.Exists(strLocalPath)) 
   System.IO.Directory.CreateDirectory(strLocalPath);
//map to a local path for editing (unless it already is mapped)
if (!wsp.IsServerPathMapped(AssemblyInfoFilePath))
   wsp.Map(AssemblyInfoFilePath, strLocalPath);

Check-out the File

//make sure I have permissions to check-out the file
if (!wsp.HasCheckInPermission)
{
   return "You do not have permissions to rev-up the project";
}
string strLocalFileName = strLocalPath + "AssemblyInfo.cs";
 
//make sure no one else has the assemblyinfo.cs file checked-out exclusively
TFS.PendingSet[] sets = wsp.QueryPendingSets(new string[] { strLocalFileName }, 
TFS.RecursionType.None, cWorkspaceName, verServer.AuthorizedUser, false);
if (sets.Length < 1)
{
   //get the file
   wsp.Get();
 
   //check-out the file for edit
   wsp.PendEdit(new string[] { strLocalFileName }, 
   TFS.RecursionType.None, null, TFS.LockLevel.CheckOut);
 
   //update the contents of the file
   strNewRev = UpdateAssemblyInfoFile(strLocalFileName);

Parse and Up the Rev

Once I’ve gotten a local copy of the Assembly info file, I find the version number, add 1, update the file and save it.

private static string UpdateAssemblyInfoFile(string filename)
{
   System.Text.RegularExpressions.MatchCollection matches;
   const string cRxMask = "\\\"[0-9]+.[0-9]+.[0-9]+.[0-9]+\\\"";
   string newVer = "";
 
   //read-in the whole AssemblyInfo file
   string strFileContent = System.IO.File.ReadAllText(filename);
 
   //use a regular expression to find the version text(s)
   matches=System.Text.RegularExpressions.Regex.Matches(strFileContent, cRxMask);
   foreach (System.Text.RegularExpressions.Match match in matches)
   {
      //increment the lowest segment (in the format: 001)
      newVer = IncrementVer(match.Value);
 
      //replace the old ver with the newer ver
      strFileContent = System.Text.RegularExpressions.Regex.Replace(
      strFileContent, match.Value, newVer);
   }
 
   //write the new content to the AssemblyInfo file
   System.IO.File.WriteAllText(filename, strFileContent);
 
   return newVer;
}
private static string IncrementVer(string oldVer)
{
   string newVer = oldVer.Replace("\"", ""); //strip string delimiters
   char[] dot = { '.' };
 
   string[] segments = newVer.Split(dot);
 
   //increment the lowest segment (in the format: 001)
   segments[3] = (int.Parse(segments[3]) + 1).ToString("000"); //3 digits
   newVer = String.Join(".", segments);
 
   return "\"" + newVer + "\""; //restore string delimiters
}

Check In

//...function DoRevUp() continued…
   //check-in the file
   wsp.CheckIn(changes, "Daily rev up");
}

Remove the Workspace

   //remove the workspace so it doesn't honk-up TFS
   verServer.DeleteWorkspace(cWorkspaceName, verServer.AuthorizedUser);
 
   return strNewRev;
}

Plan B

Occasionally, somebody from my team would check-out the AssemblyInfo file and forget to check it in, or this utility would encounter an error, etc. So I added the “if” block (near the top) to “QueryPendingSets” and if it found some, it would attempt to re-commit or respond with an error message, that somebody has this file checked-out.

else
{
   //if something goes wrong, replace re
   string re="Previous rev-up is being re-tried, successful"; 
   //if there was an error last time RevUp ran, then check-in the change now
   for (int x = 0; x < sets.Length; x++)
   {
      if (sets[x].Name == cWorkspaceName)
      {
         wsp.CheckIn(sets[x].PendingChanges, "Daily rev up retry");
         break;
      }
      else //if someone else has pending changes on the file, we should be concerned
      {
         re= "Cannot rev-up because someone else has the project checked-out " +
         "exclusively";
      }
   }
 
   return re;
}

Installing/Running

My team’s builds kicked-off at 1 am every day, so I used the Task Scheduler (on the TFS server) to run TfsRevUp a half-hour before, at 12:30 am. In the project, in the file Program.cs I checked for a command-line switch /silent. It will run the program as a command-line app which will “rev-up” and quit.

Conclusion

That is all it takes to check a file in/out of TFS and increment the build number(s).

Advanced Options

This article has covered a very simple way of incrementing project build numbers for projects in TFS. However, you may want to integrate this with a TFS build or a TFS build workflow/template. Here are a few resources that you can continue reading about to cover more advanced topics that add onto this concept:

Credit

I borrowed some of this code from other projects. These guys did some excellent work and deserve credit for some of the tricky parts of my project.

History

  • Version 1.0 (original)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here