I’m often asked about how to implement the assembly versioning during build. Back in the days, we had special build workflow activities to do so. Since TFS is supporting PowerShell scripts for pre and post build actions, we can easily implement the assembly versioning with PowerShell. This works without any modification of the build process. There are many different sample implementations available on the internet. Here I’ll present you my script.

The PowerShell Script will simply override AssemblyVersion and AssemblyFileVersion in the AssemblyInfo.cs files of the solution. This is a straight forward search and replace task. So where are we getting the version number from? There are many possibilities to generate the version number: based on date, based on revision, fixed version numbers, counters etc. etc. The easiest way to do so is using the macros which are available in the build definition UI.

image

The advantage of using the build number format to generate the assembly versions for each build is that also the build name has the same version number in it. The build number is set at the beginning of the build. If you cannot use the build number format macros, you will need to have the build workflow modified. On the other hand, if the build number does not require to contain the assembly number, you can also choose another mechanism without changing the build process template.

So, how to set it up? This is quiet simple. We need to store a PowerShell script, which does the assembly versioning, somewhere in our repository.An then we need to reference it as a pre-build extension in the build definition.

image

Quiet easy, right? So, you might wonder, what that “2” is intended to be in the scrip arguments. So the PowerShell script referenced here can be executed with parameters, like any other PowerShell script file. It depends on the projects if the AssemblyVersion and AssemblyFileVersion should be the same of if the should defer. The AssemblyFileVersion should represent the current build version and should also help you identifying a build from that number. If you want to exchange only one assembly in your deployment (i.e. in case of a bug fix) than this could be a problem because the AssemblyVersion would not be the same anymore.

This is why this script has a parameter which indicates how many digits should be used for the AssemblyVersion. In our sample, this means that the AssemblyFileVersion is 0.8.14290.1 and the AssemblyVersion is set to 0.8.0.0. So the un-used digits are zero based.

How does the PowerShell script look like?

Param(    
    [int]$digitsSetInAssemblyVersion = 4
) 

function Set-AssemblyVersionNumbers([string]$buildNumber, [string]$pathToSearch, [int]$numberOfDigitsToSetInAssemlbyVersion = 2)
{
    [string]$searchFilter = "AssemblyInfo.*"
    [regex]$pattern = "\d+\.\d+\.\d+\.\d+"

    if ($buildNumber -match $pattern -ne $true) 
    {    
        Write-Host "Could not extract a version from [$buildNumber] using pattern [$pattern]"
    } 
    else 
    {
        $extractedBuildNumber = $Matches[0]    
        Write-Host "Using version $extractedBuildNumber"     

        $buildNumberTokens = $extractedBuildNumber.Split('.')
        $assemblyVersionThirdDigit = if($numberOfDigitsToSetInAssemlbyVersion -gt 2){ $buildNumberTokens[2]} else {"0"}
        $assemblyVersionForthDigit = if($numberOfDigitsToSetInAssemlbyVersion -gt 3){ $buildNumberTokens[3]} else {"0"}

        $buildNumberAssemblyVersion = [string]::Format("{0}.{1}.{2}.{3}",$buildNumberTokens[0],$buildNumberTokens[1], $assemblyVersionThirdDigit, $assemblyVersionForthDigit)
        $buildNumberAssemblyFileVersion = [string]::Format("{0}.{1}.{2}.{3}",$buildNumberTokens[0],$buildNumberTokens[1],$buildNumberTokens[2], $buildNumberTokens[3])
        [regex]$patternAssemblyVersion = "(AssemblyVersion\("")(\d+\.\d+\.\d+\.\d+)(""\))"
        $replacePatternAssemblyVersion = "`${1}$($buildNumberAssemblyVersion)`$3"
        [regex]$patternAssemblyFileVersion = "(AssemblyFileVersion\("")(\d+\.\d+\.\d+\.\d+)(""\))"
        $replacePatternAssemblyFileVersion = "`${1}$($buildNumberAssemblyFileVersion)`$3"

        gci -Path $pathToSearch -Filter $searchFilter -Recurse | %{        
            Write-Host "  -> Changing $($_.FullName)"                 
            # remove the read-only bit on the file        
            sp $_.FullName IsReadOnly $false         
            # run the regex replace        
            (gc $_.FullName) | % { $_ -replace $patternAssemblyVersion, $replacePatternAssemblyVersion } | sc $_.FullName    
            (gc $_.FullName) | % { $_ -replace $patternAssemblyFileVersion, $replacePatternAssemblyFileVersion } | sc $_.FullName
        }     
        Write-Host "Done!"
    }
}

Set-AssemblyVersionNumbers $env:TF_BUILD_BUILDNUMBER $env:TF_BUILD_SOURCESDIRECTORY $digitsSetInAssemblyVersion

As you can see above, we are simply using the build variables in PowerShell and are replacing the AssemblyVersion and AssemblyFileVersion in all AssemblyInfo.cs files found in the source directory. If you additionally need your MSI installer to be synchronized with the assembly file version, you can also add a file version reference from the assembly to the installer product version (WiX).

image

You can also download the script from here.

No Comments