如何通过我们的CI平台(哈德森)自动递增C#程序集版本?


112

我本人和我的小组在增加程序集版本号时都感到非常恐惧,我们经常以1.0.0.0版本发布程序集。显然,这会引起很多头痛。

通过CI平台,我们的做法越来越好,我真的很想将其设置为自动递增assemblyinfo.cs文件中的值,以便随着该程序集中的代码更改自动更新程序集的版本。

我之前曾设置过(在找到Hudson之前)通过msbuild命令行或命令行增加值的方式(不记得了),但是使用Hudson,它将更新SVN存储库并触发ANOTHER构建。当哈德森每小时轮询SVN时,这将导致缓慢的无限循环。

让Hudson增加版本号是个坏主意吗?什么是替代方法呢?

理想情况下,我的解决方案标准是:

  • 在构建assemblyinfo.cs之前增加内部版本号
  • 仅增加已更改的部件中的内部版本号。这可能是不可能的,因为Hudson每次执行构建都会清除项目文件夹
  • 将更改后的assemblyinfo.cs提交到代码存储库中(当前为VisualSVN
  • 不会导致Hudson在下次扫描更改时触发新的构建

在脑海中解决这个问题,我可以很容易地通过批处理文件/命令提出解决方案,但是我的所有想法都会导致Hudson在下次扫描时触发新的构建。我并不是在寻找某人为我做任何事情,只是为我指明正确的方向,也许是让哈德森忽略某些SVN提交的技术,等等。

到目前为止,我发现的所有内容只是一篇说明如何自动增加版本号的文章,没有考虑可能会陷入无限循环的CI平台。

Answers:


63

一种简单的替代方法是通过将version属性设置为来让C#环境为您增加程序集版本major.minor.*(如AssemblyInfo文件模板中所述)。

不过,您可能正在寻找更全面的解决方案。

编辑(在评论中回答问题):

来自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.*")]

我以前从未遇到过这个问题,您能否进一步了解它的作用。它仅在一个IDE中运行还是在具有CI平台的整个开发人员团队中工作?
艾伦·赖斯

嗯,我以前看过,这可能是一个可以接受的解决方案,但是构建的#不会存储在subversion中。等等。我将不得不对该机制的工作方式进行更多研究,谢谢!您不会知道它如何确定要作为值输入的内容吗?
艾伦·赖斯

1
请参阅以下我的答案,以解决您的问题。这些值是根据构建时间确定的。
凯尔·特劳伯曼

哇,我认为这行得通。不确定我们如何忽略了这样一个简单的解决方案
艾伦·赖斯

希望能做到,很高兴我能提供帮助。当简单,快速的方法也是正确的方法时,为什么要用困难的方法呢?:)
Greg D

65

这是我为组装AssemblyFileVersion属性加盖标记的操作。

从AssemblyInfo.cs中删除了AssemblyFileVersion

向项目添加一个名为AssemblyFileInfo.cs的新的空文件。

MSBuild社区任务工具集安装在hudson构建计算机上,或作为项目中的NuGet依赖项安装

编辑项目(csproj)文件,它只是一个msbuild文件,然后添加以下内容。

某个地方会有一个<PropertyGroup>说明版本。更改它,使其读取例如

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

当项目基于hudson构建时,Hudson提供了您在此处看到的那些env变量(假设它是从subversion获取的)。

在项目文件的底部,添加

 <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>

这将使用MSBuildCommunityTasks生成AssemblyFileVersion.cs,以在构建项目之前包含AssemblyFileVersion属性。如果需要,可以对任何/所有版本属性执行此操作。

结果是,每当发布hudson构建时,生成的程序集都会获得1.0.HUDSON_BUILD_NR.SVN_REVISION的AssemblyFileVersion,例如1.0.6.2632,这意味着hudson中的第6个构建号(来自Subversion修订版2632)。


1
因此,仅更新一下:该方法适用于C#。我已经使用了一段时间了。但是C ++程序集(即C ++ / CLI)仍然是一个问题。据我所知,AssemblyInfo任务不会产生有效的C ++。另外,我认为这种方法有一个缺点,因为它对其他开发人员了解正在发生的事情有点不透明。太糟糕了,您不能将版本号作为属性直接推入MSBuild ...
CJBrew 2011年

@CJBrew您可以创建一个小的.bat文件,该文件为AssemblyInfo生成C ++代码,并使用msbuild启动该程序。我不确定将其作为属性推送意味着什么,您当然可以将版本字符串填充到您喜欢的任何属性中-您不需要使用我在这里使用的major / minor / build / revision。
2012年

与仅注释掉AssemblyFileVersion并将其设置为自动匹配[assembly:AssemblyVersion(“ 1.0。*”)]相比,使用此方法可以获得任何结果吗?
cchamberlain 2014年

@ColeChamberlain如果您是在自己的PC上而不是从Hudson在自己的PC上从Visual Studio构建的,那么它将自动递增-并且与版本号,特定的构建和源代码修订没有关系。
2014年

42

这是一个优雅的解决方案,在添加新项目时需要一些前期工作,但处理过程非常容易。

想法是每个项目都链接到仅包含程序集版本信息的解决方案文件。因此,您的构建过程仅需要更新一个文件,并且所有汇编版本都将在编译时从一个文件中提取。

脚步:

  1. 为您的解决方案文件* .cs文件添加一个类,我将其命名为min SharedAssemblyProperties.cs
  2. 从该新文件中删除所有CS信息
  3. 从AssemblyInfo文件中剪切程序集信息:[assembly:AssemblyVersion(“ 1.0.0.0”)] [assembly:AssemblyFileVersion(“ 1.0.0.0”)]
  4. 添加语句“ using System.Reflection;” 到文件,然后将数据粘贴到新的cs文件中(例如,SharedAssemblyProperties.cs)
  5. 将现有项目添加到您的项目中(等待...在添加文件之前继续阅读)
  6. 选择文件,然后单击添加,单击添加按钮旁边的下拉列表,然后选择“添加为链接”。
  7. 对解决方案中的所有现有和新项目重复步骤5和6

当您将文件添加为链接时,它将数据存储在项目文件中,并在编译时从该文件中提取程序集版本信息。

在源代码管理中,您添加了一个bat文件或脚本文件,该文件仅增加了SharedAssemblyProperties.cs文件,并且您的所有项目都会从该文件更新其程序集信息。


谢谢马克。抱歉,该死链接无效,因此社区服务器移动起来不那么容易。我应该寻求有关该主题的帮助...
sondlerd 2011年

11

可以将Hudson配置为忽略对某些路径和文件的更改,以便不提示进行新的构建。

在作业配置页上的“ 源代码管理”下,单击“ 高级”按钮。在“ 排除的区域”框中,输入一个或多个正则表达式以匹配排除的内容。

例如,要忽略对version.properties文件的更改,可以使用:

/MyProject/trunk/version.properties

这将适用于C#以外的其他语言,并允许您在Subversion中存储版本信息。


1
Hudson还可以忽略某些用户的提交,也可以不根据提交消息触发构建。这样,您可以忽略来自Hudson的所有提交。
Peter Schuetze

9

.NET为您做到这一点。在AssemblyInfo.cs文件中,将程序集版本设置为major.minor。*(例如:1.0。*)。

构建项目时,将自动生成版本。

我相信,版本号和修订号是根据日期使用unix时代生成的。生成基于当天,而修订基于自午夜以来的秒数。


21
<ring,ring>“您好,产品支持我能提供什么帮助?” <客户>“我遇到错误” <支持>“好,您正在运行什么版本?” <客户>“版本一分二修订八五二五三七四建立七四六三三五二九...” <support>“等等,只需键入它...嗯...请重复该版本数字,我们似乎没有列出该版本和修订版...”-GRRR!
Jimbo

哈哈不错的评论。我也不喜欢该递增系统:p
Joshua Hayes

3
Visual Studio中的自动增量严重不足。
Kugel

8
@Jimbo:虽然我们都同意您的评论很有趣,但实际上并不重要。在谈到VS安装时,您是否拥有Visual Studio 2008 SP1或VS2008 9.0.30729.1 SP?使用自动递增的内部版本号是一种非常常见的方案,当发行版本发布时,可以通过增加主要/次要版本号来轻松地“修复”。
马雷克(Marek)

我们使用的内部版本号最高为678,然后再重新设置为0以进行次要版本的增加(当然,cruisecontrol,它似乎比hudson更容易重设,就像cruisecontrol一样,您只是进入并保存到项目中的0) ,但哈德逊的其他一切都更好)
Dean Hiller

8

我从未真正看到1.0。*功能在VS2005或VS2008中起作用。要设置VS以增加值,是否需要做些什么?

如果AssemblyInfo.cs用1.0。*硬编码,那么实际的构建/修订存储在哪里?

在AssemblyInfo中放入1.0。*之后,我们不能使用以下语句,因为ProductVersion现在具有无效值-它使用的是1.0。*,而不是VS分配的值:

Version version = new Version(Application.ProductVersion);

叹气-这似乎是每个人都在问的那些事情之一,但是以某种方式从来没有一个可靠的答案。几年前,我看到了用于生成修订号并将其保存到AssemblyInfo中的解决方案,这是构建后过程的一部分。我希望VS2008不需要那种舞蹈。也许VS2010?


10
您必须删除AssemblyFileVersion。除此之外,它对我们来说很棒,那就是公认的答案。
艾伦·赖斯2009年

1
是的,删除AssemblyFileVersion将允许更新版本,并且不会再出现Version的错误。真好 注意:两次构建操作只会使修订版本增加一次,但是如果您重新构建,则更新版本。正如ktrauberman所说,它看起来像build.revision = date.time,它解释了为什么数据不存储在程序集中的任何地方。现在,我需要获取标准的MSI安装程序,以便在主要输出项目更新时生成新的ProductCode。安装程序不允许进行修订,只能进行构建。我想在现有安装上进行安装以进行更新。需要研究。
TonyG

5

我假设也可以使用文本模板来执行此操作,在该模板中,您可以像下面的AssemblyVersion.tt这样从环境中即时创建有问题的程序集属性。

<#@ 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#>")]

3

作为MikeS回答的继续,我想补充一点,需要安装VS + Visual Studio可视化和建模SDK才能使其正常运行,并且还需要修改项目文件。还应该提到的是,我使用Jenkins作为在带有版本模块的Windows 2008 R2服务器上运行的构建服务器,在其中获得了BUILD_NUMBER。

我的文本模板文件version.tt看起来像这样

<#@ 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#>")]

在属性组中有以下内容

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

导入Microsoft.CSharp.targets之后,我有这个(取决于安装VS的位置

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

然后,在构建服务器上,我有以下脚本在实际构建之前运行文本转换,以获取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>

这样,我就可以跟踪构建和变更集,因此,如果自上次构建以来未检查任何内容,则最后一位不应更改,但是我可能对构建过程进行了更改,因此需要倒数第二个。当然,如果您在构建之前进行了多次签入,那么您只会得到版本中反映的最新更改。我想您可以将其串联起来。

我确定您可以做些更有趣的事情,并直接从tt模板中调用TFS,但这对我有用。

然后,我可以像这样在运行时获取我的版本

Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;

1

我的解决方案不需要添加外部工具或脚本语言-可以保证在您的构建计算机上可以正常工作。我分几个部分解决了这个问题。首先,我创建了一个BUILD.BAT文件,该文件将Jenkins的BUILD_NUMBER参数转换为环境变量。我通过输入以下有关Jenkins构建的信息,使用Jenkins的“执行Windows批处理命令”功能来运行构建批处理文件:

     ./build.bat --build_id %BUILD_ID% -build_number %BUILD_NUMBER%

在构建环境中,我有一个build.bat文件,其开始如下:

     rem build.bat
     set BUILD_ID=Unknown
     set BUILD_NUMBER=0
     :parse_command_line
     IF NOT "%1"=="" (
         IF "%1"=="-build_id" (
             SET BUILD_ID=%2
             SHIFT
             )
         IF "%1"=="-build_number" (
             SET BUILD_NUMBER=%2
             SHIFT
         )
         SHIFT
         GOTO :parse_command_line
     )
     REM your build continues with the environmental variables set
     MSBUILD.EXE YourProject.sln

完成此操作后,右键单击要在Visual Studio的“解决方案资源管理器”窗格中构建的项目,然后选择“属性”,选择“构建事件”,然后输入以下信息作为“预构建事件命令行”,该命令行会自动创建一个.cs文件。包含基于当前环境变量设置的内部版本号信息:

     set VERSION_FILE=$(ProjectDir)\Properties\VersionInfo.cs
     if !%BUILD_NUMBER%==! goto no_buildnumber_set
     goto buildnumber_set
     :no_buildnumber_set
     set BUILD_NUMBER=0
     :buildnumber_set
     if not exist %VERSION_FILE% goto no_version_file
     del /q %VERSION_FILE%
     :no_version_file
     echo using System.Reflection; >> %VERSION_FILE%
     echo using System.Runtime.CompilerServices; >> %VERSION_FILE%
     echo using System.Runtime.InteropServices; >> %VERSION_FILE%
     echo [assembly: AssemblyVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%
     echo [assembly: AssemblyFileVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%

您可能需要调整自己的体味。我手动构建项目一次,以在主项目的Properties目录中生成一个初始Version.cs文件。最后,通过将Version.cs文件拖动到该项目的“属性”选项卡下方的“解决方案资源管理器”窗格中,将其手动包含到Visual Studio解决方案中。在将来的版本中,Visual Studio然后在Jenkins生成时读取该.cs文件,并从中获取正确的版本号信息。


1

因此,我们有一个项目,其中一个解决方案包含多个项目,这些项目的程序集具有不同的版本号。

在研究了上述几种方法之后,我刚刚实现了构建步骤来运行Powershell脚本,该脚本在AssemblyInfo.cs文件中进行查找和替换。我仍然在源代码管理中使用1.0。*版本号,而Jenkins只是在msbuild运行之前手动更新版本号。

dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyFileVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyFileVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }

我添加了-Encoding“ UTF8”选项,因为如果我不这样做的话,git开始将.cs文件视为二进制文件。当然,这没关系,因为我从未真正提交过结果。它只是在我测试时出现的。

我们的CI环境已经具有将Jenkins构建与特定的git commit(感谢Stash插件!)相关联的功能,因此我不必担心没有git commit及其附加的版本号。


0

这是一个更简单的机制。它仅涉及在MSBuild步骤之前添加Windows Batch命令任务生成步骤,以及使用简单的查找和替换程序(FART)。

批处理步骤

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

如果您使用的是svn以外的源代码管理,请为您的scm环境将--svn选项更改为适当的选项。

下载屁


0

我决定使用几种方法使用预构建Powershell脚本(https://gist.github.com/bradjolicoeur/e77c508089aea6614af3)来增加每个成功的构建,然后在Global.asax中进行如下操作:

  // We are using debug configuration, so increment our builds.
  if (System.Diagnostics.Debugger.IsAttached)
  {
      string version = System.Reflection.Assembly.GetExecutingAssembly()
                                                       .GetName()
                                                       .Version
                                                       .ToString();

      var psi = new ProcessStartInfo(@"svn", "commit -m \"Version: " + version + "\n \"");
      psi.WorkingDirectory = @"C:\CI\Projects\myproject";
      Process.Start(psi); 
  }

我仍然认为整个过程过于复杂,我将研究一种实现相同结果的更有效方法。我主要是因为将版本传递到SVN,然后传递给Jenkin's,而没有太多其他工具。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.