目前我只是想找到一种简单的方法来标记那些没有依赖关系的程序集的测试和维护版本。因此,我考虑在文件版本上自动递增构建和修订号,并在最终发布时将当前文件版本复制到程序集版本中。该产品正在生产中使用,但仍处于开发阶段 - 就是那种小公司,没有变更控制基础设施的情况。
版本控制是我非常热衷并花费了很长时间试图开发易于使用的版本控制系统。从您在问题中提到的内容可以清楚地看出,您已经理解了一个重要的观点,即程序集版本号与产品版本不同。一个是技术驱动,而另一个是由业务驱动。
以下假设您使用某种形式的源代码控制和构建服务器,我们使用TeamCity和Subversion/Git来举例说明。TeamCity对小型项目(10个)免费,并且是一个非常好的构建服务器,但也有其他可以完全免费使用的服务器。
版本号对不同人有不同的含义,一般结构为主版本、次版本、宏版本和微版本。我看待版本号的方式是将其分解为两部分。前半部分描述主要版本(Major)和任何关键更新(Minor)。后半部分指示它是什么时候构建的以及源代码版本是什么。不同的上下文环境中版本号也意味着不同的事情,例如 API、Web 应用程序等。
主版本.次版本.生成.修订
修订
这是从源代码控制中取出的数字,用于识别实际构建了什么。生成
这是一个不断增加的数字,可用于在构建服务器上查找特定的构建。它是一个重要的数字,因为构建服务器可能已经使用不同的参数两次构建相同的源代码。与源代码编号一起使用可以识别实际构建的内容以及如何构建。次版本
仅当公共接口有显着更改时才应更改此版本。例如,如果这是一个 API,消费代码是否仍能编译?当主版本号更改时,此数字应该被重置为零。主版本
表示您所使用的产品版本。例如,所有 VisualStudio 2008 的主版本都是 9,而 VisualStudio 2010 是 10。总有例外情况,您将不得不根据具体情况进行调整。我的原始方法是基于使用 subversion,但最近我已经转向 Git。像 Subversion 和 SourceSafe 这样使用中央存储库的源代码控制工具有一个数字,可用于识别给定时间的特定源代码集。对于分布式源代码控制(例如 Git),情况并非如此。因为 Git 使用分布式存储库,这些存储库位于每个开发机器上,所以您无法使用自动递增数字,有一个 hack 可以使用检入次数,但它很丑陋。因此,我不得不发展我的方法。
主要版本号
.次要版本号
.修订号
.构建号
修订号已经不存在了,构建号变成了修订号的位置,而宏号被插入其中。你可以随心所欲地使用宏号,但大多数情况下我会保持它不变。因为我们使用TeamCity,从修订号中丢失的信息可以在构建号中找到,这意味着需要进行两个步骤,但我们没有失去任何东西,这是可以接受的妥协。
首先要明白的是,程序集版本、文件版本和产品版本并不必须匹配。我并不主张有不同的数字集,但当对程序集进行微小的更改而不影响任何公共接口时,生活会变得轻松许多。我处理这个问题的方法是,在程序集版本中只设置主要版本号和次要版本号,但在文件版本中设置所有值。例如:
这样就可以发布热修复,不会破坏现有代码,因为程序集版本不匹配,但通过查看其文件版本号,可以看到程序集的修订/构建号。这是一种常见方法,在查看程序集详细信息时,可以在某些开源程序集上看到它。
作为团队负责人,您需要负责每当需要进行重大更改时增加次要版本号。滚动接口所需的更改但不破坏先前代码的一种解决方案是将当前接口标记为过时并创建一个新接口。这意味着现有代码被警告该方法已过时,并且可能随时被删除,但不需要立即破坏所有内容。然后可以在迁移所有内容后删除过时方法。
你可以手动执行以上所有步骤,但这将非常耗费时间,以下是我们自动化此过程的方法。每个步骤都可以运行。
AssemblyVersion
和 AssemblyFileVersion
属性。AssemblyVersion
和AssemblyFileVersion
属性添加为“0.0.0.0”。使用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>
希望我已经表达清楚了,但这里涉及到很多内容。如果有任何问题,请随时询问。我将使用任何反馈来撰写更简洁的博客文章。
你可以使用版本号的“Build”部分进行自动递增。
[assembly: AssemblyVersion("1.0.*")]
在你的环境中,测试版本是指具有构建版本号非零的版本。发布时,你会递增次要部分并将构建部分设置为0,这是你如何识别已发布程序集的方式。
如果你将程序集安装在GAC中,随着时间的推移,你的GAC会被大量不同版本的程序集淹没,所以要记住这一点。但如果你只在本地使用DLL,我认为这是一个好的做法。
在补充了 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 很有帮助。
在版本控制程序集方面,没有硬性规定,所以请随意尝试哪种方法适合您,但我建议您采用四部分的方法,因为这样您将拥有灵活性,以防将来需要进行一些更改。
例如:1.0.0.*
保留 - 这增加了额外的灵活性,以防将来需要进行任何更改。但是默认情况下,请将其保留为0。
此外,请考虑使用强密钥签署程序集。这将解决程序集冲突问题,以防您在GAC中注册了多个版本的程序集。MSDN链接
这里是一个自动生成程序集信息的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")]