如何通过CI平台(Hudson)自动增加C#程序集版本?

时间:2009-07-14 17:35:47

标签: .net msbuild continuous-integration hudson versioning

我和我的小组在增加程序集版本号时非常可怕,我们经常发送1.0.0.0版本的程序集。显然,这会引起很多麻烦。

通过我们的CI平台,我们的做法变得更好了,我真的想将其设置为自动增加assemblyinfo.cs文件中的值,以便我们的版本使用该程序集中的代码更改自动更新程序集。

我之前已经设置(在我们找到Hudson之前)通过msbuild或命令行(不记得)增加值的方法,但是使用Hudson,这将更新SVN存储库并触发另一个构建。这会导致一个缓慢的无限循环,因为Hudson每小时轮询一次SVN。

让Hudson增加版本号是一个坏主意吗?什么是另一种方法呢?

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

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

在我脑海中解决这个问题,我可以通过批处理文件/命令轻松地提出大部分解决方案,但是我的所有想法都会导致Hudson在下次扫描时触发新的构建。我不是在找人为我做一切,只是指出我正确的方向,也许是让Hudson忽略某些SVN提交的技巧等。

到目前为止,我发现的所有内容都只是一篇文章,解释了如何自动增加版本号,没有任何内容考虑到可以旋转到无限循环的CI平台。

11 个答案:

答案 0 :(得分:64)

这就是我所做的,用于标记AssemblyFileVersion属性。

从AssemblyInfo.cs

中删除了AssemblyFileVersion

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

在hudson构建计算机上安装MSBuild community tasks工具集,或在项目中安装NuGet dependency

编辑项目(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个构建#,来自颠覆修订版2632.

答案 1 :(得分:61)

一个简单的替代方法是让C#环境通过将version属性设置为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.*")]

答案 2 :(得分: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
  8. 当您将文件添加为链接时,它会将数据存储在项目文件中,并在编译时从此文件中提取程序集版本信息。

    在源代码管理中,添加一个bat文件或脚本文件,只是递增SharedAssemblyProperties.cs文件,所有项目都将从该文件更新其程序集信息。

答案 3 :(得分:11)

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

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

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

/MyProject/trunk/version.properties

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

答案 4 :(得分:9)

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

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

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

答案 5 :(得分: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?

答案 6 :(得分:5)

我假设有人可能会使用text template执行此操作,您可以在环境中动态创建有问题的程序集属性,如下面的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#>")]

答案 7 :(得分:3)

作为MikeS的答案的延续,我想补充一点,需要安装VS + Visual Studio Visualization和Modeling 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;

答案 8 :(得分:1)

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

     ./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的Solution Explorer窗格中构建的项目并选择Properties,选择Build Events,并输入以下信息作为Pre-Build Event命令行,它会根据当前环境变量设置自动创建包含内部版本号信息的.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文件,并从中获取正确的构建号信息。​​

答案 9 :(得分: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&#34; UTF8&#34;选项,因为git开始将.cs文件视为二进制文件,如果我没有。当然,这并不重要,因为我从未实际提交过结果;它就在我测试时出现了。

我们的CI环境已经有了将Jenkins构建与特定git提交相关联的工具(感谢Stash插件!),所以我不担心没有附加版本号的git提交它

答案 10 :(得分: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选项。

Download Fart