Customize Team Build 2010 – Part 10: Include Version Number in the Build Number

by Ewald Hofman 1. June 2010 11:07

In the series the following parts have been published

  1. Part 1: Introduction
  2. Part 2: Add arguments and variables
  3. Part 3: Use more complex arguments
  4. Part 4: Create your own activity
  5. Part 5: Increase AssemblyVersion
  6. Part 6: Use custom type for an argument
  7. Part 7: How is the custom assembly found
  8. Part 8: Send information to the build log
  9. Part 9: Impersonate activities (run under other credentials)
  10. Part 10: Include Version Number in the Build Number
  11. Part 11: Speed up opening my build process template
  12. Part 12: How to debug my custom activities
  13. Part 13: Get control over the Build Output
  14. Part 14: Execute a PowerShell script
  15. Part 15: Fail a build based on the exit code of a console application
  16. Part 16: Specify the relative reference path

 

When you queue a new build, the number (or name) of the build will be determined based on the BuildNumberFormat. In the 5th part of this series, the AssemblyFileVersion is increased on every run. To be able to have a relation between an assembly (for which you can retrieve the file version) and the build, I want to include the version number in the build number. This post will show you how you can achieve this.

You can change this format by clicking on the ellipsis (…) button when you edit the build definition. You see then the BuildNumber Format Editor.

image

When you click on the Macros >> button, you get a list of all available variables.

image

This list does not contain the version number, nor is it extensible to add a custom token. So we have to do something else.

Approach

The approach I will use is the following:

  1. Add the text $(Version) to the BuildNumberFormat.
  2. Read the AssemblyFileVersion from the first file that is found which applies to the AssemblyInfoFileMask (see Customize Team Build 2010 – Part 5: Increase AssemblyVersion), and store this value in a variable called Version
  3. Replace in the BulidNumberFormat the text $(Version) with the Version variable.
  4. The default workflow is continued to determine the build number based on the BuildNumberFormat

The solution for this approach consists of two classes. One to retrieve the version number and one to replace the $(Version) token.

Retrieve version number

I have chosen to get the version number directly from Source Control, because the determining of the build number is performed before the sources are retrieved locally. When the AssemblyFileVersion is found, the build part of the version number is increased because in a later stadium of the Build Process Template, the file version is increased in the files.

This results in the following class:

using System;
using System.Activities;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace BuildTasks.Activities
{
    [BuildActivity(HostEnvironmentOption.Controller)]
    public sealed class GetAssemblyVersion : CodeActivity<string>
    {
        [RequiredArgument]
        public InArgument<string> AssemblyInfoFileMask { get; set; }

        [RequiredArgument]
        public InArgument<IBuildDetail> BuildDetail { get; set; }

        protected override string Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the input arguments
            string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
            IBuildDetail buildDetail = context.GetValue(this.BuildDetail);

            var workspace = buildDetail.BuildDefinition.Workspace;
            var vc = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();

            string attribute = "AssemblyFileVersion";

            // Define the regular expression to find (which is for example 'AssemblyFileVersion("1.0.0.0")' )
            Regex regex = new Regex(attribute + @"\(""\d+\.\d+\.\d+\.\d+""\)");

            // For every workspace folder (mapping)
            foreach (var folder in workspace.Mappings)
            {
                // Get all files (recursively) that apply to the file mask
                ItemSet itemSet = vc.GetItems(folder.ServerItem + "//" + assemblyInfoFileMask, RecursionType.Full);
                foreach (Item item in itemSet.Items)
                {
                    context.TrackBuildMessage(string.Format("Download {0}", item.ServerItem));
                   
                    // Download the file
                    string localFile = Path.GetTempFileName();
                    item.DownloadFile(localFile);

                    // Read the text from the AssemblyInfo file
                    string text = File.ReadAllText(localFile);
                    // Search for the first occurrence of the version attribute
                    Match match = regex.Match(text);
                    // When found
                    if (match.Success)
                    {
                        // Retrieve the version number
                        string versionNumber = match.Value.Substring(attribute.Length + 2, match.Value.Length - attribute.Length - 4);
                        Version version = new Version(versionNumber);
                        // Increase the build number -> this will be the new version number for the build
                        Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, version.Revision);

                        context.TrackBuildMessage(string.Format("Version found {0}", newVersion));

                        return newVersion.ToString();
                    }
                }
            }

            return "No version found";
        }
    }
}
Replace token in BuildNumberFormat

As stated before I want to reuse the BuildNumberFormat capabilities to be as flexible as possible. So I add a simple activity to replace the $(Version) token.

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

namespace BuildTasks.Activities
{
    [BuildActivity(HostEnvironmentOption.Controller)]
    public sealed class UpdateVersionInBuildNumber : CodeActivity<string>
    {
        [RequiredArgument]
        public InArgument<string> BuildNumberFormat { get; set; }

        [RequiredArgument]
        public InArgument<string> VersionNumber { get; set; }

        protected override string Execute(CodeActivityContext context)
        {
            string buildNumberFormat = context.GetValue<string>(this.BuildNumberFormat);
            string versionNumber = context.GetValue<string>(this.VersionNumber);

            return buildNumberFormat.Replace("$(Version)", versionNumber);
        }
    }
}
Change the Build Process Template

Now compile the Activity project to get the new activities in the template (if that does not add the activities, then restart Visual Studio). Now open the CustomTemplate.xaml.

Select the “Update Build Number for Triggered Builds” activity, and select the Variables. Add a new variable called newVersionNumber.

image

Now add the GetAssembly to the “Update Build Number for Triggered Builds” activity (before the “Update Build Number” activity), and set the arguments to the following values.

image

Add the UpdateVersionInBuildNumber under the GetAssembly and set the arguments to the following values

image

The workflow will look like the following

image

The Build Process Template is now ready. Check in the changes to proceed to the next steps.

Update BuildNumberFormat

Next go to the build definition that makes use of the Build Process Template, and choose Edit Build Definition. Then go to the Process Tab. In the Basic group, you will find the BuildNumberFormat. By default, this is set to $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r). I want to change this to include the version number and the date. I can achieve this with the following BuildNumberFormat (but you can use any format you like):

$(BuildDefinitionName) $(Version) ($(Date:yyyyMMdd HHmm))

Now save the build definition and queue a new build. You will now see that the format of the build number is changed to something like

image


 

You can download the full solution at BuildProcess.zip. It will include the sources of every part and will continue to evolve.

Tags:

Team Build | VSTS 2010

Comments

7/1/2010 9:50:00 AM #

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        Ewald Hofman | Customize Team Build 2010 – Part 10: Include Version Number in the Build Number
        [ewaldhofman.nl]
        on Topsy.com

topsy.com |

10/8/2010 11:36:36 AM #

Greg

I'm having an issue that is proving really hard to diagnose. I used this technique to the letter and it works great most of the time. However, I have encountered some cases where the build agent won't create the workspace and perform the "Get" operation (item.DownloadFile in GetAssemblyVersion).

The symptom is that the build fails with an error to the effect of "TF209010: The build number for xxx_No version found contains invalid character(s) or is too long." Of course, that "No version found" string is the return value of GetAssemblyVersion.Execute when no files that match AssemblyFileInfoMask are found in the workspace.

Looking on the build server, sure enough... there's no workspace and, as such, there are no files. Since there are no files, of course, nothing will match the AssemblyFileInfoMask and we get the error. Like I said, the really strange thing is that the exact same code (as in the same exact set of binaries) works for some solutions, but not for others.

My source code is organized as such:

$/TeamProject
$/TeamProject/Branches
$/TeamProject/Branches/Solution123
$/TeamProject/Branches/Solution456
$/TeamProject/Branches/Solution789

My workspace mappings are like this (per build definition):

$/TeamProject/Branches/SolutionXXX --> $(SourceDir)

Like I mentioned before, some builds will work just fine (for example, Solution123 and Solution456), but with no apparent rhyme or reason some builds (Solution789 for example) fail because the agent didn't create the workspace. The only way to get it to work is if I change the mapping for that particular build definition to look like this:

$/TeamProject/Branches --> $(SourceDir)

That, of course, brings a whole lot of unnecessary source code into the build workspace and isn't a very viable solution. I'm continuing to troubleshoot, but if anyone has ideas I'd love to hear them ASAP!

Greg United States |

10/15/2010 7:25:04 AM #

Guillaume R.

Hi Ewald,
These are great posts. I'm also working and blogging on TFS 2010 and TeamBuild 2010 and wanted to let you know that i released on codeplex an activity to update AssemblyVersion and AssemblyFileVersion: http://updateassemblyinfo.codeplex.com. I put a example on how to use it to update those versions and also customize the build number.
I'll be glad to have some inputs on it from you Smile

Guillaume

Guillaume R. France |

10/17/2010 11:16:36 PM #

Ewald Hofman

Guillaume,

Very good that you are active on codeplex, but why don't you add your contribution to http://tfsbuildextensions.codeplex.com

Ewald Hofman Netherlands |

3/7/2011 12:48:07 PM #

Guillaume R.

Ed,
I've talked to Mike during the MVP Summit and i'm now a contributor on TFSBuild Extensions. I'm waiting for his review on my activity to add it to the project.
I'll try to get some time to contribute on other activities as well Smile

Guillaume R. France |

11/9/2010 5:32:36 AM #

trackback

Part 14: Execute a PowerShell script

Part 14: Execute a PowerShell script

Ewald Hofman |

11/10/2010 9:14:28 AM #

pingback

Pingback from blogs.like10.com

TechDays 2010 Presentations « Microsoft Team Foundation Server, SharePoint Server, .NET, and SQL Server

blogs.like10.com |

5/31/2011 8:22:14 AM #

trackback

Ewald’s posts on TFS Build 2010

Ewald Hofman , an ALM MVP, has written a great series of blog posts on Team Build in Team Foundation Server 2010.  The 2010 release introduces Windows Workflow as the overall orchestrator of the build process.  Ewald walks you through quite a few topics related to this and the other new features.  Check it out! Part 1: Introduction Part 2: Add arguments and variables Part 3: Use more complex arguments Part 4: Create your own activity Part 5: Increase AssemblyVersion Part 6: Use custom type for an argument Part 7: How is the custom assembly found Part 8: Send information to the build log Part 9: Impersonate activities (run under other credentials) Part 10: Include Version Number in the Build Number Part 11: Speed up opening my build process template Part 12: How to debug my custom activities Part 13: Get control over the Build Output Part 14: Execute a PowerShell script Part 15: Fail a build based on the exit code of a console application Part 16: Specify the relative reference path Technorati Tags: tfs 2010 , team build , windows workflow

Buck Hodges |

9/26/2011 11:42:38 AM #

trackback

Syncing the build number and assembly version numbers in a TFS build when using the TFSVersion activity

I was asked recently if it was possible to make the TFS build number the same format as the assembly

But it works on my PC! |

9/26/2011 12:43:41 PM #

trackback

Syncing the build number and assembly version numbers in a TFS build when using the TFSVersion activity

I was asked recently if it was possible to make the TFS build number the same format as the assembly

Richard Fennell |

4/15/2013 10:25:25 AM #

pingback

Pingback from windows-azure.net

'AssemblyInfoFileMask' is not declared. It may be inaccessible due to its protection level - Windows Azure Blog

windows-azure.net |

4/16/2013 4:02:07 PM #

pingback

Pingback from blogosfera.co.uk

The path $/FullStackSampleApplication is not a file. (type InvalidOperationException) | BlogoSfera

blogosfera.co.uk |

Comments are closed

Powered by BlogEngine.NET 1.6.1.0
Theme by Mads Kristensen


ClusterMap

Statistics

Statistics created at 09 Sep 2009

121 posts
488 comments
326 raters
1893490 visit (1043 per day)
17 users online

Recent comments

Comment RSS