Tuesday, November 30, 2010

Keep Assembly version same as build number

I have been struggling all afernoon with Team Foundation Server 2010 Custom Activities to keep the BuildNumber and the AssemblyVersions in Sync.
Apperantly System.Version is not serializable so the value gets lost when going into the Run On Agent Sequence.
As a startiong point I used the code of Ewald Hoffman. Original Article: http://www.ewaldhofman.nl/post/2010/04/20/Customize-Team-Build-2010-e28093-Part-1-Introduction.aspx
Step 1: Create the Version Generator (+ offcource the necessary unit tests Smile)…
   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace SharpSolutions.Tfs.Build.BuildTasks
   7:  {
   8:      public sealed class AssemblyVersionGenerator: IAssemblyVersionGenerator
   9:      {
  10:          /// <summary>
  11:          /// Generatas a new Assembly version
  12:          /// int the following format: Major.Minor.Build.Revision
  13:          /// Major: same as current version
  14:          /// Minor: same as current version
  15:          /// Build: 2 digit year since 2000* 1000 + day of year eg 10001 = Build run on 1 jan 2010
  16:          /// Revision: seconds since midnight
  17:          /// </summary>
  18:          /// <param name="currentVersion">used to retrieve current major and minor version</param>
  19:          /// <returns>A new Assembly version</returns>
  20:          public Version Generate(Version currentVersion)
  21:          {
  22:              //If we did not force now use the real now
  23:              //O_o get it?
  24:              DateTime now = ((IAssemblyVersionGenerator)this).Now == default(DateTime)? DateTime.Now: ((IAssemblyVersionGenerator)this).Now;
  25:              int major = currentVersion.Major;
  26:              int minor = currentVersion.Minor;
  27:              int build = (now.Year % 2000) * 1000 + now.DayOfYear;
  28:              int revision = (int)(now - new DateTime(now.Year, now.Month, now.Day, 0, 0, 0)).TotalSeconds / 10;
  29:   
  30:              return new Version(major, minor, build, revision);
  31:          }
  32:   
  33:          DateTime IAssemblyVersionGenerator.Now { get; set; }
  34:      }
  35:  }



2. Create a new Code Activity. Offcourse you can easily implement your own version of IAssemblyVersionGenerator



   1:  using System;
   2:  using System.Activities;
   3:  using Microsoft.TeamFoundation.Build.Client;
   4:  using Microsoft.TeamFoundation.Build.Workflow.Activities;
   5:   
   6:  namespace SharpSolutions.Tfs.Build.BuildTasks.Activities
   7:  {
   8:      [BuildActivity(HostEnvironmentOption.Controller)]
   9:      public sealed class GenerateAssemblyNumber : CodeActivity<string>
  10:      {
  11:          private IAssemblyVersionGenerator versionGenerator = new AssemblyVersionGenerator();
  12:          
  13:          [RequiredArgument]
  14:          public InArgument<string> AssemblyInfoFileMask { get; set; }
  15:   
  16:          [RequiredArgument]
  17:          public InArgument<string> Version { get; set; }
  18:   
  19:          [RequiredArgument]
  20:          public InArgument<IBuildDetail> BuildDetail { get; set; }
  21:          
  22:          protected override string Execute(CodeActivityContext context)
  23:          {
  24:              string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
  25:              string version = context.GetValue(this.Version);
  26:   
  27:              Version currentVersion = new Version(version);
  28:              Version newVersion = versionGenerator.Generate(currentVersion);
  29:              context.TrackBuildMessage(string.Format("New Version is {0}", newVersion.ToString()));
  30:  
  31:              return newVersion.ToString();
  32:          }
  33:      }
  34:  }



Step 3: Update the version number in the AssemblyInfo.cs



   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Activities;
   6:  using System.Text.RegularExpressions;
   7:  using System.IO;
   8:  using Microsoft.TeamFoundation.Build.Client
   9:  using Microsoft.TeamFoundation.Build.Workflow.Activities;
  10:   
  11:  namespace SharpSolutions.Tfs.Build.BuildTasks.Activities
  12:  {
  13:      [BuildActivity(HostEnvironmentOption.Agent)]
  14:      public sealed class UpdateAssemblyVersion : CodeActivity
  15:      {
  16:          [RequiredArgument]
  17:          public InArgument<string> VersionNumber { get; set; }
  18:   
  19:          [RequiredArgument]
  20:          public InArgument<string> AssemblyInfoFileMask { get; set; }
  21:          
  22:          [RequiredArgument]
  23:          public InArgument<string> SourcesDirectory { get; set; }
  24:   
  25:          // If your activity returns a value, derive from CodeActivity<TResult>
  26:          // and return the value from the Execute method.
  27:          protected override void Execute(CodeActivityContext context)
  28:          {
  29:              // Obtain the runtime value of the input arguments
  30:              string sourcesDirectory = context.GetValue(this.SourcesDirectory);
  31:              string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
  32:              Version newVersion = new Version(context.GetValue(this.VersionNumber));
  33:              
  34:              // Get all AssemblyInfo files
  35:              foreach (string file in Directory.EnumerateFiles(sourcesDirectory, assemblyInfoFileMask, SearchOption.AllDirectories))
  36:              {
  37:                  // Read the text from the AssemblyInfo file
  38:                  string assemblyInfoVersionText = File.ReadAllText(file);
  39:   
  40:                  string updatedContents = Utilities.UpdateAssemblyVersion(assemblyInfoVersionText, newVersion);
  41:   
  42:                  File.WriteAllText(file, updatedContents);
  43:              }
  44:          }
  45:   
  46:      }
  47:  }