Team Foundation Server (TFS) Team Build Custom Activity C# Code for Assembly Stamping

Posted by Bob Hardister on Geeks with Blogs See other posts from Geeks with Blogs or by Bob Hardister
Published on Wed, 14 Nov 2012 14:56:25 GMT Indexed on 2012/11/14 17:02 UTC
Read the original article Hit count: 307

Filed under:

For the full context and guidance on how to develop and implement a custom activity in Team Build see the Microsoft Visual Studio Rangers Team Foundation Build Customization Guide V.1 at http://vsarbuildguide.codeplex.com/

There are many ways to stamp or set the version number of your assemblies. This approach is based on the build number.
 
namespace CustomActivities
{
    using System;
    using System.Activities;
    using System.IO;
    using System.Text.RegularExpressions;
    using Microsoft.TeamFoundation.Build.Client;
    [BuildActivity(HostEnvironmentOption.Agent)]
    public sealed class VersionAssemblies : CodeActivity
    {
        /// <summary>
        /// AssemblyInfoFileMask
        /// </summary>
        [RequiredArgument]
        public InArgument<string> AssemblyInfoFileMask { get; set; }
        
        /// <summary>
        /// SourcesDirectory
        /// </summary>
        [RequiredArgument]
        public InArgument<string> SourcesDirectory { get; set; }
        
        /// <summary>
        ///  BuildNumber
        /// </summary>
        [RequiredArgument]
        public InArgument<string> BuildNumber { get; set; }
        /// <summary>
        /// BuildDirectory
        /// </summary>
        [RequiredArgument]
        public InArgument<string> BuildDirectory { get; set; }
        /// <summary>
        /// Publishes field values to the build report
        /// </summary>
        public OutArgument<string> DiagnosticTextOut { get; set; }
        
        // If your activity returns a value, derive from CodeActivity<TResult> and return the value from the Execute method.
        protected override void Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the input arguments
            string sourcesDirectory = context.GetValue(this.SourcesDirectory);
            string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
            string buildNumber = context.GetValue(this.BuildNumber);
            string buildDirectory = context.GetValue(this.BuildDirectory);
            // ** Determine the version number values **
            // Note: the format used here is: major.secondary.maintenance.build
            // -----------------------------------------------------------------
            // Obtain the build definition name
            int nameStart = buildDirectory.LastIndexOf(@"\") + 1;
            string buildDefinitionName = buildDirectory.Substring(nameStart);
            // Set the primary.secondary.maintenance values
            // NOTE: these are hard coded in this example, but could be sourced from a file or parsed from a build definition name that includes them
            string p = "1";
            string s = "5";
            string m = "2";
            // Initialize the build number
            string b;
            string na = "0"; // used for Assembly and Product Version instead of build number (see versioning best practices: **TBD reference)
            // Set qualifying product version information
            string productInfo = "RC2";
            // Obtain the build increment number from the build number
            // NOTE: this code assumes the default build definition name format
            int buildIncrementNumberDelimterIndex = buildNumber.LastIndexOf(".");
            b = buildNumber.Substring(buildIncrementNumberDelimterIndex + 1);
            // Convert version to integer values
            int pVer = Convert.ToInt16(p);
            int sVer = Convert.ToInt16(s);
            int mVer = Convert.ToInt16(m);
            int bNum = Convert.ToInt16(b);
            int naNum = Convert.ToInt16(na);
            // ** Get all AssemblyInfo files and stamp them **
            // Note: the mapping of AssemblyInfo.cs attributes to assembly display properties are as follows: 
            //      - AssemblyVersion = Assembly Version  - used for the assembly version (does not change unless p, s  or m values are changed)  
            //      - AssemblyFileVersion = File Version  - used for the file version (changes with every build)
            //      - AssemblyInformationalVersion = Product Version - used for the product version (can include additional version information)
            // ------------------------------------------------------------------------------------------------------------------------------------------------
            Version assemblyVersion = new Version(pVer, sVer, mVer, naNum);
            Version newAssemblyFileVersion = new Version(pVer, sVer, mVer, bNum);
            Version productVersion = new Version(pVer, sVer, mVer);
            // Setup diagnostic fields
            int numberOfReplacements = 0;
            string addedAssemblyInformationalAttribute = "No";
            // Enumerate over the assemblyInfo version attributes
            foreach (string attribute in new[] { "AssemblyVersion", "AssemblyFileVersion", "AssemblyInformationalVersion" })
            {
                // Define the regular expression to find in each and every Assemblyinfo.cs files (which is for example 'AssemblyVersion("1.0.0.0")' )
                Regex regex = new Regex(attribute + @"\(""\d+\.\d+\.\d+\.\d+""\)");
                foreach (string file in Directory.EnumerateFiles(sourcesDirectory, assemblyInfoFileMask, SearchOption.AllDirectories))
                {
                    string text = File.ReadAllText(file);   // Read the text from the AssemblyInfo file    
                    // If the AsemblyInformationalVersion attribute is not in the file, add it as the last line of the file 
                    // Note: by default the AssemblyInfo.cs files will not contain the AssemblyInformationalVersion attribute
                    if (!text.Contains("[assembly: AssemblyInformationalVersion(\""))
                    {
            string lastLine = Environment.NewLine + "[assembly: AssemblyInformationalVersion(\"1.0.0.0\")]";
                        text = text + lastLine;
                        addedAssemblyInformationalAttribute = "Yes";
                    }
                    // Search for the expression
                    Match match = regex.Match(text);        
                    if (match.Success)                     
                    {
                        // Get file attributes
                        FileAttributes fileAttributes = File.GetAttributes(file);
                        // Set file to read only
                        File.SetAttributes(file, fileAttributes & ~FileAttributes.ReadOnly);
                        // Insert AssemblyInformationalVersion attribute into the file text if does not already exist
                        string newText = string.Empty;
                        if (attribute == "AssemblyVersion")
                        {
                            newText = regex.Replace(text, attribute + "(\"" + assemblyVersion + "\")");
                            numberOfReplacements++;
                        }
                        if (attribute == "AssemblyFileVersion")
                        {
                            newText = regex.Replace(text, attribute + "(\"" + newAssemblyFileVersion + "\")");
                            numberOfReplacements++;
                        }
                        if (attribute == "AssemblyInformationalVersion")
                        {
                            newText = regex.Replace(text, attribute + "(\"" + productVersion + " " + productInfo + "\")");
                            numberOfReplacements++;
                        }
                        // Publish diagnostics to build report (diagnostic verbosity only)
                        context.SetValue(this.DiagnosticTextOut, "  Added AssemblyInformational Attribute: " + addedAssemblyInformationalAttribute +
                                                                "  Number of replacements: " + numberOfReplacements +
                                                                "  Build number: " + buildNumber +
                                                                "  Build directory: " + buildDirectory +
                                                                "  Build definition name: " + buildDefinitionName +
                                                                "  Assembly version: " + assemblyVersion +
                                                                "  New file version: " + newAssemblyFileVersion +
                                                                "  Product version: " + productVersion +
                                                                "  AssemblyInfo.cs Text Last Stamped: " + newText);
                        // Write the new text in the AssemblyInfo file
                        File.WriteAllText(file, newText);
                        // restore the file's original attributes
                        File.SetAttributes(file, fileAttributes);
                    }
                }
            }
        }
    }
}

© Geeks with Blogs or respective owner