维护程序集版本号的最佳实践/指南

我正在寻找关于如何管理。NET 程序集。Product 版本是最简单的,因为这似乎通常是由业务决定的。然后,文件版本似乎用于部署之间的版本控制,其中实际的程序集版本仅在发布时使用。

现在,我只是在寻找一种简单的方法来标记一个程序集的测试和维护版本,而这些测试和维护版本并不依赖于这个程序集,所以我正在查看文件版本上自动递增的构建和修订号,对于最终版本,将当前文件版本复制到程序集版本。该产品正在生产使用中,但仍在开发中-你知道-这些小公司之一,没有变更控制基础设施的情况。

62184 次浏览

您可以使用版本号的 Build 部分进行自动增量。

[assembly: AssemblyVersion("1.0.*")]

在您的环境中,测试版本是具有构建版本的版本!= 0.在发布时,增加次要部分并将构建部分设置为0,这就是识别已发布程序集的方法。

如果你在 GAC 中安装程序集,随着时间的推移,你的 GAC 会充斥着许多不同的版本,所以请记住这一点。但是如果只在本地使用 dls,我认为这是一个很好的实践。

在美国,[ AssemblyVersion ]是一个非常重要的东西。NET.微软鼓励的一种理念是让它自动增量,强制依赖程序集的 所有项目重新编译。如果使用构建服务器,工作正常。这从来都不是 错了要做的事情,但是要小心那些带着剑的人。

另一个与其实际含义更密切相关的是,该数字代表程序集的公共接口的版本控制。换句话说,只有在更改公共接口或类时才会更改它。因为只有这样的更改才需要重新编译程序集的客户端。不过,这需要手动完成,因为构建系统不够智能,无法自动检测到这样的更改。

只有当程序集部署在您无法到达的计算机上时,才可以增加版本,从而进一步扩展此方法。这就是微软使用的方法。NET 程序集版本号很少更改。主要是因为这会给他们的客户带来巨大的痛苦。

因此,微软所宣扬的并不是它所实践的。它的构建过程和版本控制是无与伦比的,他们甚至有一个专门的软件工程师来监控这个过程。WaitHandle 这招不太管用。WaitOne (int)重载,特别是 造成了相当大的痛苦。固定好了。NET 4.0使用了一种非常不同的方法,但是这有点超出了范围。

这完全取决于您和您对如何很好地控制构建过程和发布周期的信心,从而做出自己的选择。除此之外,自动递增[ AssemblyFileVersion ]是非常合适的。然而,不便的是,这是 没有支持。

对于程序集的版本控制没有硬性规定,所以你可以随意尝试任何适合你的方法,但是我建议你使用4个部分的方法,因为你将会有灵活性,如果你想在将来做一些改变的话。

For ex: 1.0.0. *

保留-这增加了额外的灵活性,如果你想在未来作出任何改变。但作为默认值,保持为0。

此外,请考虑使用强密钥对程序集进行签名。如果在 GAC 中注册了多个版本的程序集,这将解决程序集冲突问题。MSDN 链路

版本控制是我非常热衷的事情,并且花了很长时间试图想出一个易于使用的版本控制系统。从您在问题中已经说过的内容可以清楚地看出,您已经理解了一个重要的问题,即程序集版本号并不等同于产品版本号。一个是技术驱动,另一个是业务驱动。

下面假设您使用某种形式的源代码管理和生成服务器。对于上下文,我们使用 团队城市和 Subversion/Git。TeamCity 对于少数(10)个项目是免费的,并且是一个非常好的构建服务器,但是还有其他的,其中一些是完全免费的。

版本号是什么意思

版本对一个人的意义可能对另一个人的意义不同,一般结构是大的,小的,宏观的,微观的。我查看版本号的方法是将其分为两部分。前半部分描述了主版本(Major)和任何关键更新(Small)。后半部分指出它是何时构建的以及源代码版本是什么。版本号也意味着不同的东西取决于上下文,它是一个 API,Web 应用程序,等等。

Major.Minor.Build.Revision

  • Revision这是从源代码管理获取的数字,用于识别 实际上是建成的。
  • 这是一个不断增加的数字,可以用来找到一个 构建服务器上的特定构建。 这是一个重要的数字,因为 构建服务器可能已经构建了相同的 两次使用不同的 中使用内建编号 与源号连接 可以让你确定建造了什么 以及怎么做到的。
  • 这应该只有当有一个重大的变化 公共接口。例如,如果它是一个 API,消费代码仍然是 能够编译? 这个数字应该重置为零时,主要的数字变化。
  • Major指示 你所使用的产品,例如 VisualStudio2008专业 程序集是9和 VisualStudio2010 是10。

规则的例外

规则总是有例外的,当你遇到它们的时候,你将不得不去适应它们。我最初的方法是基于使用 subversion 的,但最近我转向了 Git。源代码控制,比如使用中央存储库的 subversion 和 Source safe,有一个数字可以用来标识给定时间的特定源集。对于像 Git 这样的分布式源代码控件来说,情况并非如此。因为 Git 使用每台开发机器上的分布式存储库,所以不能使用自动递增的数字,所以有一种方法使用签入的数字,但是这种方法很难看。正因为如此,我不得不改进我的方法。

Major.Minor.Macro.Build

修订号现在已经没有了,build 已经转移到了修订之前的位置,并且宏已经被插入。您可以使用您认为合适的宏,但大多数情况下我不会使用它。因为我们使用 TeamCity,从修订号中丢失的信息可以在构建中找到,这确实意味着有一个两步过程,但是我们没有丢失任何东西,这是一个可以接受的折衷方案。

设置什么

首先要理解的是,程序集版本、文件版本和产品版本不必匹配。我并不是提倡使用不同的数字集,但是当对程序集进行小的更改时,这会使生活变得容易得多,因为这不会影响您不必重新编译依赖程序集的任何公共接口。我处理这个问题的方法是只在组装版本中设置主要和次要数字,但在文件版本中设置所有值。例如:

  • 1.2.0.0(AssemblyVersion)
  • 1.2.3.4(FileVersion)

这使您能够推出不会破坏现有代码的热修复程序,因为程序集版本不匹配,但允许您通过查看程序集的文件版本号来查看程序集的修订/构建。这是一种常见的方法,当您查看程序集详细信息时,可以在一些开放源代码程序集上看到这种方法。

您作为团队的领导,需要负责在需要进行重大更改时增加次要数量。对接口进行必要更改但不破坏以前的代码的一种解决方案是将当前的代码标记为过时并创建一个新的接口。这意味着警告现有代码该方法已过时,可以在任何时候删除,但不要求您立即中断所有内容。然后,当所有内容都已迁移时,您可以删除过时的方法。

怎么连接

您可以手动完成以上所有工作,但这将非常耗费时间,以下是我们如何自动化这个过程。每个步骤都是可运行的。

  • 从所有项目 AssemblyInfo.cs 文件中删除 AssemblyVersionAssemblyFileVersion属性。
  • 创建一个公共汇编信息文件(称为 VersionInfo.cs) ,并将其作为链接项添加到所有项目中。
  • AssemblyVersionAssemblyFileVersion属性添加到值为“0.0.0.0”的版本中。
  • 创建生成解决方案文件的 MsBuild 项目。
  • 在更新 VersionInfo.cs 的构建之前添加一个任务。有许多开源的 MsBuild 库,其中包含可以设置版本号的 AssemblyInfo 任务。只要将它设置为任意数字并进行测试。
  • 添加一个属性组,该属性组包含生成号中每个段的属性。这是你设置大调和小调的地方。构建和修订号应作为参数传入。

颠覆:

<PropertyGroup>
<Version-Major>0</Version-Major>
<Version-Minor>0</Version-Minor>
<Version-Build Condition=" '$(build_number)' == '' ">0</Version-Build>
<Version-Build Condition=" '$(build_number)' != '' ">$(build_number)</Version-Build>
<Version-Revision Condition=" '$(revision_number)' == '' ">0</Version-Revision>
<Version-Revision Condition=" '$(revision_number)' != '' ">$(revision_number)</Version-Revision>
</PropertyGroup>

希望我已经说得很清楚了,但是还有很多事情需要处理。有什么问题尽管问。我将使用任何反馈来把一个更简洁的博客文章放在一起。

添加到 布朗姆斯基回答,我想指出的是,在 Semver.org的语义版本2.0标准之后,Major.Minor.Build.Revision将是非法的,因为在增加一个数字之后,所有右边的常规值都必须重置为零。

遵循该标准的一个更好的方法是使用 Major.Minor+Build.Revision。这显然不适合在 AssemblyVersionAttribute中使用,但可以使用自定义属性或静态类。

TeamCity 中的 Semver 应该可以使用 Meta-runner Power Pack 获得。对于带有 git-flow 的 git (特别是在。NET 世界) ,我发现 GitVersion是有帮助的。

这里 是用于自动生成装配信息的 T4模板示例。每次执行转换时,程序集版本都会递增。你应该只填写你的项目数据:

// Names.
string projectName = "MyProjectName"; // Project (short name for COM).
string productName = "My Project Name"; // Full title.
string developerName = "Developer Name"; // Developer.
string assemblyType = "Application"; // Application, Library, etc.


// Version.
int majorVersion = 1;
int minorVersion = 0;


// Year of the start of work on the project.
string since = "2021";

在转换模板之后,您将得到如下输出:

#if COMVISIBLE
using System.EnterpriseServices;
#endif
using System.Reflection;
using System.Runtime.InteropServices;


// General information about this assembly is provided by the following set
// attributes. Change the values of these attributes to change the information,
// related to the assembly.
[assembly: AssemblyTitle ("My Project Name Application 1.0")] // Assembly name.
[assembly: AssemblyDescription ("My Project Name Application 1.0")] // Assembly description.
[assembly: AssemblyCompany ("Developer Name")] // Developer.
[assembly: AssemblyProduct ("Developer Name My Project Name Application")] // Product name.
[assembly: AssemblyCopyright ("© Developer Name 2021")] // Copyright.
//[assembly: AssemblyTrademark ("Developer Name ® My Project Name Application®")] // Trademark.
[assembly: AssemblyCulture ("")]
[assembly: AssemblyVersion ("1.0.2110.0047")]
[assembly: AssemblyFileVersion ("1.0.2110.0047")]
#if DEBUG
[assembly: AssemblyConfiguration ("Debug")]
#else
[assembly: AssemblyConfiguration ("Release")]
#endif


// Setting ComVisible to False makes the types in this assembly invisible
// for COM components. If you need to refer to the type in this assembly via COM,
// set the ComVisible attribute to TRUE for this type.
#if COMVISIBLE
[assembly: ComVisible (true)]
[assembly: ApplicationName ("MyProjectName")] // The name of the COM application.
[assembly: ApplicationID ("fc24620a-239d-4e40-b756-7ed38e82ef69")]
#else
[assembly: ComVisible (false)]
#endif
// The following GUID is used to identify the type library if this project will be visible to COM
[assembly: Guid ("e60d1ecf-6c7b-4c9b-925f-4bf07615da87")]