语义版本控制

给定版本号 MAJOR.MINOR.PATCH,版本号递增规则:

  • 当你进行不兼容的API更改时,递增 MAJOR 版本
  • 当你以向后兼容的方式添加功能时,递增 MINOR 版本
  • 当您进行向后兼容的错误修复时,递增 PATCH 版本

介绍

在软件管理领域,存在一个令人生畏的地方,叫做“依赖地狱”。你的系统越大,你整合到软件中的软件包越多,你就越有可能在某一天发现自己陷入这个绝望的境地。

在具有许多依赖项的系统中,发布新软件包版本可能会迅速变成一场噩梦。如果依赖项规范过于严格,那么您将面临版本锁定(无法升级软件包而无需发布每个依赖项软件包的新版本)的危险。如果指定的依赖项过于宽松,那么您将不可避免地受到版本滥用的困扰(假设与未来版本的兼容性比实际情况要合理得多)。当版本锁定和/或版本滥用阻止您轻松安全地推进项目时,您就会陷入依赖地狱。

作为解决此问题的一种方案,我们提出了一组简单的规则和要求,规定了如何分配和增加版本号。这些规则基于但不一定局限于在封闭式和开源软件中广泛使用的现有通用做法。为了使此系统能够正常运行,您首先需要声明一个公共 API。这可能包括文档或由代码本身强制执行。无论哪种情况,重要的是这个 API 必须清晰而精确。一旦确定了公共 API,您就可以通过特定的版本号增量来传达对它的更改。考虑一个版本格式为 X.Y.Z(主版本号.次版本号.补丁版本号)的版本。不影响 API 的 bug 修复将增加补丁版本号,向后兼容的 API 添加/更改将增加次版本号,而不向后兼容的 API 更改将增加主版本号。

我们称这个系统为“语义化版本控制”。在此方案中,版本号及其变更方式可以传达底层代码的含义以及从一个版本到下一个版本所进行的修改。

语义版本控制规范 (SemVer)

本文件中的关键字“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应根据RFC 2119中的描述进行解释。

  1. 使用语义版本控制的软件必须声明一个公共API。这个API可以在代码本身中声明,或者严格存在于文档中。无论如何,它都应该精确且全面。
  2. 一个正常的版本号必须采取X.Y.Z的形式,其中X、Y和Z都是非负整数,并且不得包含前导零。X是主版本号,Y是次版本号,Z是补丁版本号。每个元素都必须以数字方式递增。例如:1.9.0 -> 1.10.0 -> 1.11.0。
  3. 一旦发布了一个版本化的包,那么该版本的内容就绝不能修改。任何修改都必须作为新版本发布。
  4. 主要版本0(0.y.z)用于初始开发。 任何事情都可能随时发生变化。 不应将公共API视为稳定的。
  5. 版本 1.0.0 定义了公共API。在此版本发布后,版本号递增的方式取决于此公共API及其变化情况。
  6. 如果仅引入向后兼容的 bug 修复,则补丁版本 Z(x.y.Z | x > 0)必须递增。bug 修复是指用于纠正不正确行为的内部更改。
  7. 次要版本 Y(x.Y.z | x > 0)如果向公共 API 引入了新的向后兼容功能,则必须递增。如果任何公共 API 功能被标记为已弃用,则必须递增。如果私有代码中引入了大量新功能或改进,则可以递增。它可以包含补丁级别的更改。当次要版本递增时,补丁版本必须重置为 0。
  8. 如果公共 API 中引入了任何向后不兼容的更改,则必须递增主版本号 X(X.y.z | X > 0)。它还可能包括次要版本和补丁级别的更改。当主版本号递增时,必须将补丁版本和次要版本重置为 0。
  9. 预发布版本可以通过在补丁版本后附加一个连字符和一系列由点分隔的标识符来标记。标识符只能包含ASCII字母数字字符和连字符[0-9A-Za-z-]。标识符不能为空。数字标识符不能包含前导零。预发布版本的优先级低于相关的正常版本。预发布版本表示该版本不稳定,可能不满足由其相关的正常版本表示的预期兼容性要求。示例:1.0.0-alpha,1.0.0-alpha.1,1.0.0-0.3.7,1.0.0-x.7.z.92,1.0.0-x-y-z.--。
  10. 构建元数据可以通过在补丁或预发行版本之后立即添加一个加号以及一系列由点分隔的标识符来表示。标识符必须仅包含ASCII字母数字字符和连字符[0-9A-Za-z-]。标识符不能为空。在确定版本优先级时,必须忽略构建元数据。因此,仅在构建元数据上有所不同的两个版本具有相同的优先级。示例:1.0.0-alpha+001,1.0.0+20130313144700,1.0.0-beta+exp.sha.5114f85,1.0.0+21AF26D3----117B344092BD。
  11. 优先级是指当进行排序时,版本之间如何进行比较。
    1. 优先级必须按照主要、次要、补丁和预发行标识符的顺序来计算(构建元数据不会影响优先级)。
    2. 比较这些标识符的优先顺序时,从左到右依次进行,其第一差值决定了优先顺序,即主版本、次版本和补丁版本总是以数字方式进行比较。
    3. 当主版本号、次版本号和补丁版本号相等时,预发行版本的优先级低于正常版本:示例:1.0.0-alpha < 1.0.0。
    4. 对于具有相同的主版本号、次版本号和补丁版本号的两个预发行版本,其优先级必须通过从左到右比较每个以点分隔的标识符来确定,直到找到差异为止,如下所示:
      1. 仅由数字组成的标识符将按数字进行比较。
      2. 使用字母或连字符的标识符将按ASCII排序顺序进行字典比较。
      3. 数字标识符的优先级总是低于非数字标识符。
      4. 如果所有前面的标识符都相等,则预发布字段较大的集合的优先级高于预发布字段较小的集合。示例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。

为什么要使用语义版本控制?

这并不是一个新的或革命性的想法。事实上,你可能已经在做类似的事情了。问题是“接近”并不足够好。如果没有遵守某种正式的规范,版本号对于依赖管理来说基本上毫无用处。通过给上述想法命名和明确定义,就可以很容易地将你的意图传达给你的软件用户。一旦这些意图明确,最终就可以制定灵活(但不太灵活)的依赖规范。

一个简单的例子可以说明语义化版本控制如何让依赖地狱成为过去。考虑一个名为“Firetruck”的库。它需要一个名为“Ladder”的语义化版本控制包。在创建Firetruck时,Ladder的版本是3.1.0。由于Firetruck使用了一些在3.1.0中首次引入的功能,因此您可以安全地将Ladder的依赖项指定为大于或等于3.1.0但小于4.0.0。现在,当Ladder版本3.1.1和3.2.0可用时,您可以将其发布到您的包管理系统中,并知道它们将与现有的依赖软件兼容。

作为一个负责任的开发者,你当然想要验证任何包升级是否都如广告所说那样有效。但现实世界是复杂混乱的,我们对此无能为力,只能保持警惕。不过,你可以利用语义化版本控制来提供一种合理的方式来发布和升级包,而无需滚动依赖包的新版本,这将为你节省时间和麻烦。

如果以上所有内容听起来都很有用,那么您只需要声明您将开始使用语义版本控制,然后遵循相关规则即可。将您的自述文件与此网站链接,以便其他人了解规则并从中受益。

FAQ

1、在0.y.z初始开发阶段我应该如何处理修订版本?

最简单的做法是从 0.1.0 开始初始开发版本 ,然后递增每个后续版本的次要版本。

2、我如何知道何时发布 1.0.0?

如果你的软件正在用于生产,那么它的版本应该是1.0.0。如果你拥有用户已经依赖的稳定API,那么你的版本应该是1.0.0。如果你正在担心大量的向后兼容问题,那么你的版本可能已经是1.0.0了。

3、这不会阻碍快速发展和快速迭代吗?

主版本零(Major version zero)的核心在于快速开发。如果你每天都在更改 API,那么你应该仍处在版本 0.y.z 中,或者在单独的开发分支上工作以准备下一个主版本。

4、如果公共API的向后不兼容的更改即使是最小的也需要主要版本号的提升,那么我会不会很快就达到42.0.0版本?

这是一个负责任的发展和前瞻性发展的问题。不应该轻易地将不兼容的更改引入有大量依赖代码的软件中。升级的成本可能很大。不得不通过升级主要版本来发布不兼容的更改,意味着您将思考更改的影响,并评估所涉及的成本/效益比。

5、记录整个公共 API 的工作量太大了!

作为一名专业的开发人员,你有责任妥善记录供他人使用的软件。管理软件的复杂性是保持项目高效运行的重要组成部分,如果没有人知道如何使用你的软件,或者哪些方法是安全的调用,那么这一点很难实现。从长远来看,语义版本控制和坚持定义良好的公共 API 可以使每个人和每件事都运行顺畅。

6、如果我不小心将向后不兼容的更改作为次要版本发布,该怎么办?

一旦你发现你已经违反了语义版本控制规范,请立即修复问题并发布一个新的次版本,以修正问题并恢复向后兼容性。即使在这种情况下,也不允许修改已发布的版本。如果合适的话,请记录违规的版本,并向用户通报问题,以便他们了解违规的版本。

7、如果我更新了自己的依赖项而没有改变公共API,我应该怎么做?

这将被视为兼容的,因为它不会影响公共 API。明确依赖与您的包相同的依赖项的软件应该有自己的依赖项规范,并且作者会注意到任何冲突。确定更改是补丁级别还是次要级别修改取决于您是否更新了依赖项以修复错误或引入新功能。我们通常期望后者有额外的代码,在这种情况下,这显然是一个次要的增量。

8、如果我无意中改变了公共 API,并且这种方式不符合版本号更改(例如,代码在补丁发布中错误地引入了一个重大破坏性更改),那么会有什么后果呢?

运用你最好的判断力。如果你有一个庞大的受众群体,其行为将受到公共 API 更改的严重影响,那么最好进行主版本发布,尽管这种修复可以严格地被视为补丁发布。请记住,语义版本控制主要是通过版本号的变化来传达意义。如果这些更改对你的用户很重要,请使用版本号来通知他们。

9、我应该如何处理弃用功能?

弃用现有功能是软件开发中正常的一部分,并且通常需要进行这种操作以取得进步。当你弃用公共 API 的一部分时,你应该做两件事:

(1)更新你的文档,以便让用户了解这一变化,

(2)在弃用现有功能的基础上发布一个新的次要版本。在你在新的主要版本中完全删除该功能之前,应该至少发布一个包含弃用功能的次要版本,以便用户可以平稳过渡到新的 API。

10、SemVer 对版本字符串有大小限制吗?

不,但要有良好的判断力。例如,一个长度为255个字符的版本字符串可能是过度了。此外,特定系统可能会对字符串的大小施加自己的限制。

11、“v1.2.3”是语义版本吗?

不,“v1.2.3”不是语义版本。然而,在语义版本前加上“v”是一种常见的方式(在英语中)来表示它是一个版本号。将“version”缩写为“v”在版本控制中经常看到。例如:git tag v1.2.3 -m "Release version 1.2.3",在这种情况下,“v1.2.3”是一个标签名,而语义版本是“1.2.3”。

12、有没有推荐的正则表达式(RegEx)来检查SemVer字符串?

有两个。一个具有支持它们的系统的命名组 (PCRE [Perl 兼容正则表达式,即 Perl、PHP 和 R]、Python 和去)。

See: regex101: build, test, and debug regex
C:Hatps://regek101.com/r/li7o1s/3/

^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

 

还有一个带有编号的捕获组(所以 cg1 = 主要,cg2 = 次要, cg3 = 补丁,cg4 = 预发行版,cg5 = buildmetadata) 兼容 使用 ECMA 脚本 (JavaScript)、PCRE(Perl 兼容正则表达式、 即 Perl、PHP 和 R)、Python 和 Go。

See: regex101: build, test, and debug regex
C:hatps://regek101.com/r/wikis/1/

^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

 语义版本控制规范最初由 Gravatar 发明者和 GitHub 联合创始人 Tom Preston-Werner 撰写。如果您想留下反馈,请在 GitHub 上打开一个问题

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值