首页 文章

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

提问于
浏览
109

我自己和我的团队在增加程序集版本号时非常可怕,我们经常发送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平台 .

12 回答

  • 1

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

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

    EDIT (回复评论中的问题):

    来自 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.*")]
    
  • 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上构建时(假设它是从subversion中获取的),Hudson提供了那些你在那里看到的env变量 .

    在项目文件的底部,添加

    <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的buit .

  • 1

    这是一个优雅的解决方案,在添加新项目时需要先做一些工作,但很容易处理这个过程 .

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

    脚步:

    • 向解决方案文件* .cs文件添加一个类,我命名为min SharedAssemblyProperties.cs

    • 从该新文件中删除所有cs信息

    • 从AssemblyInfo文件中剪切程序集信息:[assembly:AssemblyVersion("1.0.0.0")] [assembly:AssemblyFileVersion("1.0.0.0")]

    • 将语句"using System.Reflection;"添加到文件中,然后将数据粘贴到新的cs文件中(来自SharedAssemblyProperties.cs)

    • 将现有项目添加到项目中(等待...在添加文件之前继续阅读)

    • 选择文件,在单击“添加”之前,单击“添加”按钮旁边的下拉列表,然后选择"Add As Link" .

    • 对于中的所有现有项目和新项目重复步骤5和6解

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

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

  • 3

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

    在作业配置页面的 Source Code Management 下,单击 Advanced 按钮 . 在 Excluded Regions 框中输入一个或多个正则表达式以匹配排除项 .

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

    /MyProject/trunk/version.properties
    

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

  • 8

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

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

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

  • 5

    我从来没有真正看到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?

  • 9

    我假设有人也可以使用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#>")]
    
  • 42

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

    我在 properties 组中有以下内容

    <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;
    
  • 0

    我的解决方案不需要添加外部工具或脚本语言 - 几乎可以保证在您的构建机器上工作 . 我在几个部分解决了这个问题 . 首先,我创建了一个BUILD.BAT文件,将Jenkins BUILD_NUMBER参数转换为环境变量 . 我使用Jenkins的“执行Windows批处理命令”功能通过输入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文件并从中获取正确的构建号信息 .

  • 61

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

    在研究了上述几种方法之后,我刚刚实现了一个构建步骤来运行一个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提交相关联的工具(感谢Stash插件!),所以我不担心没有附加版本号的git提交 .

  • 11

    这是一种更简单的机制 . 它只涉及在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

  • 0

    我决定使用一些使用prebuild 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 .

相关问题