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

159
我正在寻求关于如何管理 .NET 程序集的三个不同版本号的指引、建议,甚至是命令。产品版本号最简单,通常由业务决定。文件版本号用于在部署之间进行版本控制,而实际程序集版本号仅在发布时使用。
目前我只是想找到一种简单的方法来标记那些没有依赖关系的程序集的测试和维护版本。因此,我考虑在文件版本上自动递增构建和修订号,并在最终发布时将当前文件版本复制到程序集版本中。该产品正在生产中使用,但仍处于开发阶段 - 就是那种小公司,没有变更控制基础设施的情况。

1
看看这个... http://www.codinghorror.com/blog/2007/02/whats-in-a-version-number-anyway.html - Rahul Soni
6个回答

221

版本控制是我非常热衷并花费了很长时间试图开发易于使用的版本控制系统。从您在问题中提到的内容可以清楚地看出,您已经理解了一个重要的观点,即程序集版本号与产品版本不同。一个是技术驱动,而另一个是由业务驱动。

以下假设您使用某种形式的源代码控制和构建服务器,我们使用TeamCity和Subversion/Git来举例说明。TeamCity对小型项目(10个)免费,并且是一个非常好的构建服务器,但也有其他可以完全免费使用的服务器。

版本号的含义

版本号对不同人有不同的含义,一般结构为主版本、次版本、宏版本和微版本。我看待版本号的方式是将其分解为两部分。前半部分描述主要版本(Major)和任何关键更新(Minor)。后半部分指示它是什么时候构建的以及源代码版本是什么。不同的上下文环境中版本号也意味着不同的事情,例如 API、Web 应用程序等。

主版本.次版本.生成.修订

  • 修订 这是从源代码控制中取出的数字,用于识别实际构建了什么。
  • 生成 这是一个不断增加的数字,可用于在构建服务器上查找特定的构建。它是一个重要的数字,因为构建服务器可能已经使用不同的参数两次构建相同的源代码。与源代码编号一起使用可以识别实际构建的内容以及如何构建。
  • 次版本 仅当公共接口有显着更改时才应更改此版本。例如,如果这是一个 API,消费代码是否仍能编译?当主版本号更改时,此数字应该被重置为零。
  • 主版本 表示您所使用的产品版本。例如,所有 VisualStudio 2008 的主版本都是 9,而 VisualStudio 2010 是 10。

规则之外的例外

总有例外情况,您将不得不根据具体情况进行调整。我的原始方法是基于使用 subversion,但最近我已经转向 Git。像 Subversion 和 SourceSafe 这样使用中央存储库的源代码控制工具有一个数字,可用于识别给定时间的特定源代码集。对于分布式源代码控制(例如 Git),情况并非如此。因为 Git 使用分布式存储库,这些存储库位于每个开发机器上,所以您无法使用自动递增数字,有一个 hack 可以使用检入次数,但它很丑陋。因此,我不得不发展我的方法。

主要版本号次要版本号修订号构建号

修订号已经不存在了,构建号变成了修订号的位置,而宏号被插入其中。你可以随心所欲地使用宏号,但大多数情况下我会保持它不变。因为我们使用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 任务,可以设置版本号。只需将其设置为任意数字并进行测试即可。
  • 添加一个属性组,其中包含构建编号各个部分的属性。这是您设置主版本和次版本的位置。构建和修订号应作为参数传递。

使用Subversion:

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

希望我已经表达清楚了,但这里涉及到很多内容。如果有任何问题,请随时询问。我将使用任何反馈来撰写更简洁的博客文章。


你有没有考虑使用GitHub的版本标签?我非常好奇它如何适应这个谜题。 - Jón Trausti Arason
1
@raRaRa - 这是一个相当古老的帖子。虽然我仍然坚持其中大部分内容,但有些事情我会做得不同。NuGet版本控制已经改变了我的工作方式,我确实使用Git标签来进行成功构建,但归根结底,程序集上的版本号应该与构建服务器上的构建版本和源代码控制中的标签版本相对应。 - Bronumski

60
[AssemblyVersion] 在 .NET 中非常重要。微软鼓励的一种理念是让它自动递增,强制 所有 依赖于该程序集的项目重新编译。如果使用构建服务器,这种方法可以运行良好。但这永远不会是做错的事情,注意不要惹怒人们。
另一种更贴近其实际含义的方法是,该数字代表程序集的公共接口版本控制。换句话说,只有在更改公共接口或类时才更改它。因为只有这样的更改需要重新编译程序集的客户端。但这需要手动完成,构建系统无法智能检测到此类更改。
您还可以通过仅在程序集部署在无法控制的机器上时递增版本来进一步扩展此方法。这是微软使用的方法,他们的 .NET 程序集版本号很少更改。主要是因为这会给他们的客户带来很大的痛苦。
所以,微软所讲的并不是他们实践的。他们的构建过程和版本控制却是无与伦比的,他们甚至有一个专门的软件工程师监控这个过程。然而,WaitHandle.WaitOne(int) 这个方法特别 引起了相当大的痛苦。在 .NET 4.0 中使用了非常不同的方法进行修复,但这有点超出了范围。
这取决于你自己的信心和控制构建过程以及发布周期的能力,来做出自己的选择。除此之外,自动递增 [AssemblyFileVersion] 是非常合适的。但是不方便的是,这种方式并不被支持。

11

你可以使用版本号的“Build”部分进行自动递增。

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

在你的环境中,测试版本是指具有构建版本号非零的版本。发布时,你会递增次要部分并将构建部分设置为0,这是你如何识别已发布程序集的方式。

如果你将程序集安装在GAC中,随着时间的推移,你的GAC会被大量不同版本的程序集淹没,所以要记住这一点。但如果你只在本地使用DLL,我认为这是一个好的做法。


我喜欢发布版本的0构建号。 - ProfK
1
当然,这意味着您的程序集强名称将随每次构建而更改,无论您是否希望如此。 - Richard

10

在补充了 Bronumskis回答 后,我想指出,根据 semver.org 的语义化版本2.0标准,Major.Minor.Build.Revision 是不合法的,因为在增加一个数字后,所有右侧的常规值都必须重置为零。

遵循标准的更好方法是使用 Major.Minor+Build.Revision。这显然不能用于 AssemblyVersionAttribute,但可以使用自定义属性或静态类代替。

在 TeamCity 中,可以使用 Meta-runner Power Pack 来获取 Semver。对于使用 git-flow 的 git(尤其是在 .NET 世界中),我发现 GitVersion 很有帮助。


2
有趣,我会去查看一下。你提到的版本号格式可以用在AssemblyInformationalVersion属性中。 - Bronumski

1

在版本控制程序集方面,没有硬性规定,所以请随意尝试哪种方法适合您,但我建议您采用四部分的方法,因为这样您将拥有灵活性,以防将来需要进行一些更改。

例如:1.0.0.*

保留 - 这增加了额外的灵活性,以防将来需要进行任何更改。但是默认情况下,请将其保留为0。

此外,请考虑使用强密钥签署程序集。这将解决程序集冲突问题,以防您在GAC中注册了多个版本的程序集。MSDN链接


0

这里是一个自动生成程序集信息的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")]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接