I'm working on a C#/VB.Net project that uses SVN and TeamCity build server. A dozen or so assemblies are produced by the build. I want to control the assembly versions so that they all match up and also match the TeamCity build label.
I've configured TeamCity to use a build label of
Major.Minor.{Build}.{Revision}
Where Major and Minor are constants that I set manually, {Revision} is determined by the SVN repository version at checkout and {Build} is a TeamCity auto-incrementing build counter. So an example build label would be
2.5.437.4423
What techniques would you suggest to ensure that all of the assembly versions match the TeamCity build label?
We're using CruiseControl.net and SVN. We drive it the other way. We are using the MSBuildCommunityTasks Version task in an MSBuild script to increment the version number for CI builds and using that version number to tag the source code.
EDIT: Asked for more detail on MSBuild targets...
We use a separate script that is for the CI build and is not used for the developer builds. We tried using different targets in the MSBuild files that studio uses as project files but this got to be a headache and required manual editing of files that studio was generating.
The structure of the MSBuild file is pretty straightforward:
Import extra pieces
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!-- contains some variables that set project names, paths etc. -->
<Import Project="Properties.msbuild"/>
BeforeBuild: set new version number and rewrite the AssemblyInfo file
<Version VersionFile="$(VersionFile)" BuildType="None" RevisionType="Increment">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<!--Modify Assembly Info-->
<AssemblyInfo CodeLanguage="CS"
OutputFile="Properties\AssemblyInfo.cs"
AssemblyTitle="$(TargetAssembly)"
AssemblyDescription="$(AssemblyDescription) svn:@(SanitizedSvnUrl) revision:$(SvnRevision)"
AssemblyCompany="Your company name"
AssemblyProduct="Name of product"
AssemblyCopyright="Copyright © your company 2009"
ComVisible="false" Guid="$(WindowGuid)"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
Condition="$(Revision) != '0' " />
Build: build the actual project file MSBuild script in release mode
AfterBuild: we run our unit test projects (as a guard against creating tags for broken builds in the next steps), use the SvnInfo tasks and some RegexReplace tasks to set some variables up with paths and tag names, and use the SvnCopy task to create the tag.
<SvnCopy UserName="username"
Password="password"
SourcePath="@(SvnTrunkPath)"
DestinationPath="@(SvnTagsPath)/BUILD-$(TargetAssembly)-$(Major).$(Minor).$(Build).$(Revision)" Message="Tagging successful build" />
Even though this is already having an accepted answer, I would like to add an idea we have used.
For large projects there may be many assembly info files to update, and with constant re-factoring there is always the risk that the build script is not aware of new files that needs to be updated.
For this reason we have created a simple code file which defines a constant for the actual version and then all projects inherit this constant and us in the assembly info file. Then the build script need only update one file.
Another benefit here is that this is also speeding up the build process somewhat since it does not need to go through many files and change.
I'd suggest using TeamCity's AssemblyInfo patcher build feature:
http://confluence.jetbrains.net/display/TCD65/AssemblyInfo+Patcher
Just create your projects from VisualStudio, configure the build feature in the BuildSteps page (see http://confluence.jetbrains.net/display/TCD65/Adding+Build+Features), and as long as you keep the default AssemblyInfo.cs file, it will work.
This approach is working great for me.
Advantages:
Disadvantages:
I'm leaving Hamish's answer as the accepted answer, but for completeness I thought it would be worth documenting the approach we finally adopted.
I maintain 2 seperate build configurations in TeamCity, one is the CI build and another is a build that runs weekly when there are any changes (or manually) and is our official release build. I went for the two-build approach because the build takes perhaps 40 minutes end-to-end and I wanted developers to get a quick feedback on any build issues when they commit changes. The CI build therefore is a subset of the full release build but it does build all of the code. The versioning also differs between the two builds:
The reason for this somewhat arbitrary choice is to allow us to clearly and simply distinguish between CI builds and Release builds.
We have a solution-wide file called AssemblyVersionInfo that is included (soft linked) in every project in the solution. The file contains (in essence) this:
So developer builds all use the same static and readily identifiable version number, which is higher than anything produced by the build server so that in a versioning conflict, the developer's file wins.
On the build server, we us MSBuild Community Tasks to generate a new AssemblyVersionInfo file that overwrites the default content. We use the TeamCity build string as the new version. In this way, we can easily distinguish between the following 3 situations:
In another twist, note that I am setting
AssemblyFileVersion
, notAssemblyVersion
. We force our assembly versions to be a static, fixed version string of 'Major.Minor.0.0'. We do this so that we are able to issue bug fix releases without having to worry about versioning issues. The AssemblyFileVersion lets us figure out what build a user has installed without it being part of the assembly's identity.