How can I auto increment the C# assembly version v

2019-01-12 15:21发布

Myself and my group are horrendous at incrementing assembly version numbers and we frequently ship assemblies with 1.0.0.0 versions. Obviously, this causes a lot of headaches.

We're getting a lot better with our practices via our CI platform and I'd really like to set it up to auto increment the values within the assemblyinfo.cs file so that the versions of our assemblies are auto updated with the code changes in that assembly.

I had previously setup (before we found Hudson) a way to increment the value through either msbuild or the command line (can't remember), but with Hudson, that will update the SVN repository and trigger ANOTHER build. That would result in a slow infinite loop as Hudson polls SVN every hour.

Is having Hudson increment the version number a bad idea? What would be an alternative way to do it?

Ideally, my criteria for a solution would be one that:

  • Increments the build number in assemblyinfo.cs before a build
  • Only increments the build number in assemblies that have changed. This may not be possible as Hudson wipes out the project folder every time it does a build
  • Commits the changed assemblyinfo.cs into the code repository (currently VisualSVN)
  • Does not cause Hudson to trigger a new build the next time it scans for changes

Working this out in my head, I could easily come up with a solution to most of this through batch files / commands, but all of my ideas would cause Hudson to trigger a new build the next time it scans. I'm not looking for someone to do everything for me, just point me in the right direction, maybe a technique to get Hudson to ignore certain SVN commits, etc.

Everything I've found so far is just an article explaining how to get the version number automatically incremented, nothing takes into account a CI platform that could be spun into an infinite loop.

12条回答
萌系小妹纸
2楼-- · 2019-01-12 15:32

Hudson can be configured to ignore changes to certain paths and files so that it does not prompt a new build.

On the job configuration page, under Source Code Management, click the Advanced button. In the Excluded Regions box you enter one or more regular expression to match exclusions.

For example to ignore changes to the version.properties file you can use:

/MyProject/trunk/version.properties

This will work for languages other than C# and allows you to store your version info within subversion.

查看更多
Explosion°爆炸
3楼-- · 2019-01-12 15:37

This is a simpler mechanism. It simply involves the addition of a Windows Batch command task build step before the MSBuild step and the use of a simple find and replace program (FART).

The Batch Step

fart --svn -r AssemblyInfo.cs "[assembly: AssemblyVersion(\"1.0.0.0\")]" "[assembly: AssemblyVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
fart --svn -r AssemblyInfo.cs "[assembly: AssemblyFileVersion(\"1.0.0.0\")]" "[assembly: AssemblyFileVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
exit /b 0

If you are using source control other than svn change the --svn option for the appropriate one for your scm environment.

Download Fart

查看更多
乱世女痞
4楼-- · 2019-01-12 15:39

I am assuming one might also do this with a text template where you create the assembly attributes in question on the fly from the environment like AssemblyVersion.tt does below.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("SVN_REVISION");
revision = revision == null ? "0" : int.Parse(revision).ToString();    
#>
using System.Reflection;
[assembly: AssemblyVersion("1.0.<#=build#>.<#=revision#>")]
[assembly: AssemblyFileVersion("1.0.<#=build#>.<#=revision#>")]
查看更多
Viruses.
5楼-- · 2019-01-12 15:41

As a continuation of MikeS's answer I wanted to add that VS + Visual Studio Visualization and Modeling SDK needs to be installed for this to work, and you need to modify the project file as well. Should also be mentioned I use Jenkins as build server running on a windows 2008 R2 server box with version module, where I get the BUILD_NUMBER.

My Text Template file version.tt looks like this

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("_BuildVersion");
revision = revision == null ? "5.0.0.0" : revision;    
#>
using System.Reflection;
[assembly: AssemblyVersion("<#=revision#>")]
[assembly: AssemblyFileVersion("<#=revision#>")]

I have the following in the Property Groups

<PropertyGroup>
    <TransformOnBuild>true</TransformOnBuild>
    <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

after import of Microsoft.CSharp.targets, I have this (dependant of where you install VS

<Import Project="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />

On my build server I then have the following script to run the text transformation before the actual build, to get the last changeset number on TFS

set _Path="C:\Build_Source\foo"

pushd %_Path% 
"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" history . /r /noprompt /stopafter:1 /Version:W > bar
FOR /f "tokens=1" %%foo in ('findstr /R "^[0-9][0-9]*" bar') do set _BuildVersion=5.0.%BUILD_NUMBER%.%%foo
del bar
popd

echo %BUILD_NUMBER%
echo %_BuildVersion%
cd C:\Program Files (x86)\Jenkins\jobs\MyJob\workspace\MyProject
MSBuild MyProject.csproj /t:TransformAll 
...
<rest of bld script>

This way I can keep track of builds AND changesets, so if I haven't checked anything in since last build, the last digit should not change, however I might have made changes to the build process, hence the need for the second last number. Of course if you make multiple check-ins before a build you only get the last change reflected in the version. I guess you could concatenate of that is required.

I'm sure you can do something fancier and call TFS directly from within the tt Template, however this works for me.

I can then get my version at runtime like this

Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;
查看更多
Anthone
6楼-- · 2019-01-12 15:42

A simple alternative is to let the C# environment increment the assembly version for you by setting the version attribute to major.minor.* (as described in the AssemblyInfo file template.)

You may be looking for a more comprehensive solution, though.

EDIT (Response to the question in a comment):

From AssemblyInfo.cs:

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
查看更多
贼婆χ
7楼-- · 2019-01-12 15:42

Here's what I did, for stamping the AssemblyFileVersion attribute.

Removed the AssemblyFileVersion from AssemblyInfo.cs

Add a new, empty, file called AssemblyFileInfo.cs to the project.

Install the MSBuild community tasks toolset on the hudson build machine or as a NuGet dependency in your project.

Edit the project (csproj) file , it's just an msbuild file, and add the following.

Somewhere there'll be a <PropertyGroup> stating the version. Change that so it reads e.g.

 <Major>1</Major>
 <Minor>0</Minor>
 <!--Hudson sets BUILD_NUMBER and SVN_REVISION -->
 <Build>$(BUILD_NUMBER)</Build>
 <Revision>$(SVN_REVISION)</Revision>

Hudson provides those env variables you see there when the project is built on hudson (assuming it's fetched from subversion).

At the bottom of the project file, add

 <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')" />
  <Target Name="BeforeBuild" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')">
    <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
    <AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyFileInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyConfiguration="$(Configuration)" Condition="$(Revision) != '' " />
  </Target>

This uses the MSBuildCommunityTasks to generate the AssemblyFileVersion.cs to include an AssemblyFileVersion attribute before the project is built. You could do this for any/all of the version attributes if you want.

The result is, whenever you issue a hudson build, the resulting assembly gets an AssemblyFileVersion of 1.0.HUDSON_BUILD_NR.SVN_REVISION e.g. 1.0.6.2632 , which means the 6'th build # in hudson, buit from the subversion revision 2632.

查看更多
登录 后发表回答