[SEMVER]语义版本规范

semver/semver.org提出一个语义版本规范,用于规范版本的生成和设置

英文版:Semantic Versioning 2.0.0

中文版:语义化版本 2.0.0

优势

  1. 标识当前应用版本信息
  2. 管理代码仓库
  3. 管理外部依赖

版本格式

版本号命名如下:

# 英文版
MAJOR.MINOR.PATCH
# 中文版
主版本号.次版本号.修订号

版本号递增规则:

  1. 主版本号(MAJOR version):出现不兼容的API变化
  2. 次版本号(MINOR version):新增向后兼容的功能
  3. 修订号(补丁版本号,PATCH version):修复向后兼容的bug

规范

  1. 使用语义版本控制的软件必须(MUST)声明公共APIAPI可以在代码本身中声明,或者严格存在于文档中。不管怎样,它都应该是精确和全面的
  2. 正常版本号必须(MUST)采用X.Y.Z格式,其中X、YZ是非负整数,并且必须不(MUST NOT)包含前导零。X是主版本,Y是次版本,Z是补丁版本。每个元素必须(MUST)以数字形式增加。例如:1.9.0 -> 1.10.0 -> 1.11.0
  3. 一旦发布了版本化的包,就不能再(MUST NOT)修改该版本的内容。任何修改都必须作为新版本重新发布
  4. 主版本号为0(0.y.z)表示初始开发阶段,可以执行任意的修改。这个阶段的公共API不应该(SHOULD NOT)被视为稳定版。
  5. 从版本1.0.0定义的公共API开始,之后版本号的递增方式依赖于此公共API的更改方式
  6. 只要有向后兼容的bug被修复,就必须(MUST)递增补丁版本号Z(x.y.Z | x>0)。错误修复被定义为修复错误行为的内部更改
  7. 只要有向后兼容的功能被引入公共API,就必须(MUST)递增次版本号Y(x.Y.z | x>0)。只要有任何公共API被标记为舍弃(deprecated),就必须(MUST)递增次版本号。如果在私有代码中引入了大量新功能或改进,其中可以(MAY)包括补丁级别的改变,那么可以(MAY)递增次版本号。当次版本号递增时必须(MUST)设置补丁版本号为0
  8. 只要有不向后兼容的改变被引入公共API,就必须(MUST)递增主版本号X(X.y.z | x>0),其中可以(MAY)包括次版本和补丁级别的修改。当主版本号递增时,必须(MUST)设置次版本号和补丁版本号为0
  9. 预发布版本可以(MAY)通过在补丁版本之后附加连字符(-)和一系列点(.)分隔的标识符来表示。标识符必须(MUST)只包含ASCII字母数字和连字符[0-9a-za-z-]。标识符必须不(MUST NOT)为空。数字标识符必须不(MUST NOT)包含前导0。预发布版本的优先级低于相关的正常版本。预发布版本表示该版本尚不稳定,可能不满足与其关联的正常版本预期的兼容性要求。比如,1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92
  10. 构建元数据可以(MAY)通过在补丁或预发布版本之后立即附加一个加号和一系列点分隔的标识符来表示。标识符必须(MUST)只包含ASCII字母数字和连字符[0-9a-za-z-]。标识符必须不(MUST NOT)为空。在确定版本优先级时应该(SHOULD)忽略构建元数据。因此如果有两个版本仅在构建元数据上有区别,那么这两个版本优先级相同。比如,1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85
  11. 优先级是指在排序时版本之间如何进行比较。必须(MUST)按顺序将版本分离为主、次、补丁和预发布标识符来计算优先级(构建元数据不包含在优先级中)。当从左到右比较每个标识符时,优先级由第一个差异决定,如下所示:主、次和补丁版本总是用数字进行比较。比如,1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。当主、次和补丁的大小相同时,预编译版本的优先级低于正常版本。比如,1.0.0-alpha < 1.0.0。具有相同主版本、次版本和补丁版本的两个预发布版本的优先级必须(MUST)通过从左到右比较每个点分隔的标识符来确定,直到发现以下差异:仅由数字组成的标识符用数字进行比较,带字母或连字符的标识符按ASCII排序顺序在词法上进行比较。数字标识符总是优先级低于非数字标识符。如果前面的所有标识符都相同,则拥有更长字段集的预发布版本的优先级高于较小字段集的预发布版本。比如,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

FAQ

问:应该如何处理0.y.z初始开发阶段的修订?

最简单的方式是初始开发版本从0.1.0开始,然后在每一个后续的阶段递增次版本号

问:什么时候发布1.0.0版本?

下列情况可用于发布1.0.0版本

  1. 软件已经在生产中使用
  2. 已经有用户依赖于稳定的API
  3. 开发过程中非常担心向后兼容性

问:是否语义化规范不鼓励快速开发和快速迭代?

主版本为0的初始开发阶段就是用于快速开发的。如果每天都在改变API,那么软件应该仍旧停留在0.y.z阶段或者在一个单独的开发分支上处理下一个主版本

问:是否对公共API小小的不兼容修改都要递增主版本,那这样的话很快就能达到42.0.0

这就要考验你的开发经验和开发远见。不应将不兼容的更改轻易地引入具有大量依赖代码的软件中,升级所需的成本可能很高。必须推出主版本以发布不兼容的更改意味着你已经考虑了更改的影响,并评估所涉及的成本/效益比

问:为全部公共API生成文档需要做太多的工作了!

为了其他人的使用而去文档化软件,这是作为职业开发者的责任。管理软件复杂性是保持项目高效的一个非常重要的部分,如果没有人知道如何使用您的软件,或者可以安全地调用哪些方法,这很难做到。从长远来看,语义版本控制以及对定义良好的公共API的坚持可以使每个人都能顺利运行

问:如果不小心在次版本发布了一个不兼容改变该怎么办?

一旦意识到已经破坏了语义化版本规范,修复问题并发布一个新的次要版本,以纠正问题并恢复向后兼容性。即使在这种情况下,修改已发行的版本也是不可接受的。如果合适,记录违规版本并告知用户该问题,以便他们了解有问题的版本。

问:如果我更新了依赖,但是没有改变公共API,应该怎么做?

这种方式是兼容的,因为它没有影响公共API。明确依赖于你的包的软件应该有它们自己的依赖规范,作者也会注意到任何冲突。决定是否这个修改是补丁级别还是次版本级别依赖于你更新你的依赖是为了修复bug还是添加新功能。我通常期待额外的代码是为了后一种,这种情况下明显是一个次版本级别递增

问:如果我不小心修改了公共API,但是不兼容于版本号的改变(比如在补丁版本中错误的引入了主版本级别的不兼容修改)?

运用你最好的判断。如果有大量的用户会因为这个公共API的返回而受到影响,那么最好的策略是执行一次主版本发布,即使这次修复严格上被视为是补丁发布。记住,语义版本化就是通过版本号的变化来传达意义。如果这些更改对您的用户很重要,请使用版本号通知他们

问:应该如何处理待舍弃的功能?

舍弃已存在的功能是软件开发的常规动作,也是为了进一步发展所必须的。当你舍弃部分公共API,应该做两件事情:(1)更新文档让用户知道这次改变,(2)发布一个新的次版本,仍旧包含这个舍弃功能。在新的主版本发布中完全移除这些功能之前,应该至少发布一个包含这些舍弃功能的次版本,其目的是让用户能够平滑的迁移到新的API

问:语义规范对版本字符串有大小限制吗?

没有限制,但是255个字符的版本号也太长了一点。此外,特定系统可能会对字符串的大小有限制

辅助工具

semantic-release/semantic-release

conventional-changelog/standard-version