The Team Build service in Team Foundation Server includes the current date in the build number by default. To me, the string looks like the random gibberish of a disk head crash.
FooBarBuild_20060928.1
FooBarBuild_20060928.2
…
I know some people are fond of including the date in a build label, but it's a turn off for me. Build labels have a tendency to show up in many places, and a friendly number is easier on the eyes.
FooBar_2.5.1
FooBar_2.5.2
…
FooBar_2.5.176
Fortunately, it's easier to change Team Build with a custom MSBuild task. There are some examples of how to do this out there, but none that generate a truly friendly name. Ideally, the task will start with a base name like "FooBar_2.5" and just generate an extra identity digit. One of the properties in play during a team build is LastBuildNumber, which we can inspect during the task and use to generate the build number we want. The code would look something like this:
using System;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
namespace TfsBuildUtilities
{
public class GetBuildNumber : Task
{
public override bool Execute()
{
bool result = true;
try
{
ValidateProperties();
string buildNumber = GetLastBuildNumber();
_buildNumber = BaseBuildName + "." + buildNumber;
}
catch (Exception e)
{
result = false;
BuildErrorEventArgs eventArgs;
eventArgs = new BuildErrorEventArgs
(
"",
"",
BuildEngine.ProjectFileOfTaskNode,
BuildEngine.LineNumberOfTaskNode,
BuildEngine.ColumnNumberOfTaskNode,
0, 0,
"GetBuildNumber failed: " + e.Message,
"", ""
);
BuildEngine.LogErrorEvent(eventArgs);
throw;
}
return result;
}
private string GetLastBuildNumber()
{
string buildNumber = null;
// if there is no last build, or it looks as if the
// last build was not using our name,
// we will reset to ".1";
if (String.IsNullOrEmpty(LastBuildNumber) ||
!LastBuildNumber.StartsWith(BaseBuildName))
{
buildNumber = "1";
}
// otherwise we need to parse out the last number and increment it
else
{
string[] parts = LastBuildNumber.Split('.');
int number = 1;
bool parseResult = Int32.TryParse(
parts[parts.Length - 1],
out number
);
if (parseResult)
{
number++;
buildNumber = number.ToString();
}
}
if (String.IsNullOrEmpty(buildNumber))
{
throw new InvalidOperationException(
"Could not generate a valid build number"
);
}
return buildNumber;
}
private void ValidateProperties()
{
if (String.IsNullOrEmpty(BaseBuildName))
{
throw new ArgumentException("BaseBuildName is null");
}
}
[Output]
public string BuildNumber
{
get { return _buildNumber; }
set { _buildNumber = value; }
}
private string _buildNumber = String.Empty;
[Required]
public string BaseBuildName
{
get { return _baseBuildName; }
set { _baseBuildName = value; }
}
private string _baseBuildName;
public string LastBuildNumber
{
get { return _lastBuildNumber; }
set { _lastBuildNumber = value; }
}
private string _lastBuildNumber;
}
}
Then, register the task in TFSBuild.proj file. I like to deploy the assembly into the MSBuild extensions path to use from multiple projects.
<UsingTask
TaskName="TfsBuildUtilities.GetBuildNumber"
AssemblyFile="$(MSBuildExtensionsPath)\MyDir\TfsBuildUtilities.dll"/>
Finally, in the same .proj file, override the build number target to spit a new build label into the system.
<Target Name = "BuildNumberOverrideTarget" >
<GetBuildNumber BaseBuildName="FooBar_2.5"
LastBuildNumber="$(LastBuildNumber)">
<Output TaskParameter="BuildNumber"
PropertyName="BuildNumber"/>
</GetBuildNumber>
</Target>
TFS is making the CM job easy…