How can I auto increment the C# assembly version via our CI platform (Hudson)?

我和我的团队在递增程序集版本号方面非常糟糕,我们经常发布1.0.0.0版本的程序集。显然,这会带来很多麻烦。

通过我们的 线人平台,我们的实践越来越好,我真的很想设置为自动递增 assemblyinfo.cs文件中的值,这样我们的程序集的版本就可以随着程序集中代码的变化而自动更新。

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

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

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

  • Increments the build number in assemblyinfo.cs before a build
  • 只递增已更改的程序集中的生成号。这可能是不可能的,因为 Hudson 在每次构建时都会清除项目文件夹
  • 将更改后的 Assemblyinfo.cs 提交到代码存储库(当前为 VisualSVN)
  • 不会导致 Hudson 在下次扫描更改时触发新的构建

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

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

120479 次浏览

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

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

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

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

NET 可以为您做到这一点。在 AssemblyInfo.cs 文件中,将汇编版本设置为 Major. small. * (例如: 1.0. *)。

When you build your project the version is auto generated.

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

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

On the job configuration page, under 源代码管理, click the 高级 button. In the 禁区 box you enter one or more regular expression to match exclusions.

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

/MyProject/trunk/version.properties

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

我从没见过那个1.0版本。* VS2005或 VS2008的特写工作。是否需要做些什么来设置 VS 以增加值?

If AssemblyInfo.cs is hardcoded with 1.0.*, then where are the real build/revision stored?

在放入1.0之后。* 在 AssemblyInfo,我们不能使用以下语句,因为 ProductVersion 现在有一个无效值——它使用1.0。* 而不是 VS 赋予的值:

Version version = new Version(Application.ProductVersion);

唉——这似乎是每个人都会问的问题之一,但不知为何从来没有一个可靠的答案。几年前,我看到了一些解决方案,可以生成一个修订号并将其保存到 AssemblyInfo 中,作为构建后过程的一部分。我希望 VS2008不需要这种舞蹈。也许是 VS2010?

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

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

步骤:

  1. 给你的解决方案文件 * . cs 文件添加一个类,我命名为 min sharedassemblyproperties.cs
  2. 从新文件中删除所有 cs 信息
  3. 从 AssemblyInfo 文件中删除程序集信息: [ Assembly: AssemblyVersion (“1.0.0.0”)] [ Assembly: AssemblyFileVersion (“1.0.0.0”)]
  4. 在文件中添加“ using System. response”语句,然后将数据粘贴到新的 cs 文件中( sharedassemblyproperties.cs )
  5. 将现有项目添加到项目中(请等待... ... 在添加文件之前继续读取)
  6. 选择该文件,然后在单击 Add 之前,单击 Add 按钮旁边的下拉列表,并选择“ Add As Link”。
  7. 对解决方案中的所有现有项目和新项目重复步骤5和6

When you add the file as a link, it stores the data in the project file and upon compilation pulls the assembly version information from this one file.

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

下面是我所做的,用于戳记 AssemblyFileVersion 属性。

从 AssemblyInfo.cs 中删除 AssemblyFileVersion

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

在 Hudson 构建机器上安装 MSBuild 社区任务工具集,或者在项目中作为 NuGet 依赖性安装。

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

某个地方会有一个 <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 属性。如果需要,可以对任何/所有版本属性执行此操作。

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.

这是一个更简单的机制。它只涉及在 MSBuild 步骤之前添加 Windows 批处理命令任务生成步骤和使用简单的查找和替换程序(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

如果您正在使用源代码管理而不是 svn,请为您的 scm 环境的相应选项更改—— svn 选项。

下载屁

我假设人们也可以使用一个 文本模板来实现这个功能,你可以像下面的 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#>")]

作为 MikeS 回答的延续,我想补充一点,VS + Visual Studio Visualization and ModelingSDK 需要安装才能工作,而且您还需要修改项目文件。还应该提到的是,我使用 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.target 之后,我有了这个(取决于您安装 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>

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.

我相信您可以做一些更好的事情,并直接从 tt 模板中调用 TFS,但是这对我来说是有用的。

然后我可以像这样在运行时得到我的版本

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

我的解决方案不需要添加外部工具或脚本语言——它几乎可以保证在构建机器上工作。我分几个部分解决这个问题。首先,我创建了一个 BUILD.bat 文件,它将 Jenkins BUILD _ NUMBER 参数转换为一个环境变量。我使用 Jenkins 的“ Execute Windows batch command”函数来运行构建批处理文件,为 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

完成后,右键单击要在 VisualStudio 的解决方案资源管理器窗格中生成的项目并选择“属性”,选择“生成事件”,然后输入以下信息作为“预生成事件命令行”,该命令行将自动创建一个。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%

You may need to adjust to your build taste. I build the project manually once to generate an initial Version.cs file in the Properties directory of the main project. Lastly, I manually include the Version.cs file into the Visual Studio solution by dragging it into the Solution Explorer pane, underneath the Properties tab for that project. In future builds, Visual Studio then reads that .cs file at Jenkins build time and gets the correct build number information out of it.

我决定使用一些方法,使用一个预构建 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);
}

I still think the whole process is overcomplicated and I'm going to look into a more efficient method of achieving the same result. I wanted this mainly for passing the version into SVN and then into Jenkin's without too many addtional tools.

因此,我们有一个包含一个解决方案的项目,该解决方案包含几个具有不同版本号的程序集的项目。

在研究了上面的几个方法之后,我刚刚实现了一个构建步骤来运行一个 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 提交与版本号附加到它。