语义版本2.0.0

总结

给定版本号MAJOR。轻微。PATCH,递增:

  1. 进行不兼容的API更改时的主要版本
  2. 在向后兼容中添加功能时的MINOR版本方式
  3. 修复向后兼容错误时的PATCH版本

预发布和生成元数据的其他标签作为扩展提供向少校致敬。轻微。PATCH格式。

介绍

在软件管理的世界里,有一个可怕的地方,叫做“依赖地狱”。你的系统越大,你的包越多集成到您的软件中,您越有可能发现自己在这绝望的深渊里。

在具有许多依赖项的系统中,发布新的包版本可以快速成为一场噩梦。如果依赖项规范太紧,则您处于版本锁定的危险(无需升级包就无法升级发布每个依赖包的新版本)。如果依赖项是指定得太松散,您将不可避免地受到版本混乱的影响(假设与更多未来版本的兼容性超出合理范围)。当版本锁定和/或版本混乱时,依赖地狱就在这里阻止您轻松安全地推进项目。

为了解决这个问题,我们提出了一组简单的规则和规定如何分配和增加版本号的需求。这些规则基于但不一定局限于现有规则在封闭和开放源代码软件中广泛使用的通用实践。要使这个系统工作,首先需要声明一个公共API。这可能是由文档组成或由代码本身执行。无论如何,确实如此重要的是,该API必须清晰准确。一旦你确定了你的公众身份API,您可以使用特定增量将更改传达给您的版本数字。考虑X.Y.Z(Major.Minor.Patch)的版本格式。Bug未修复影响API增加补丁版本,向后兼容API添加/更改增加了次要版本和向后不兼容的API更改会增加主版本。

我们称这个系统为“语义版本控制”。在这个方案下,版本号以及它们的更改方式传达了底层代码的含义以及已从一个版本修改为下一个版本。

语义版本规范(SemVer)

关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHILL”、“SHALL NOT”和“SHULD”,本文件中的“不应”、“建议”、“可能”和“可选”应为解释如中所述副本请求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. 修补程序版本Z(x.y.Z | x>0)如果只向后,则必须递增引入了兼容的错误修复。错误修复被定义为内部修正错误行为的更改。

  7. 次要版本Y(x.Y.z | x>0)如果是新的,则必须向后递增将兼容功能引入公共API。它一定是如果任何公共API功能标记为弃用,则递增。可能是这样如果引入了大量新功能或改进,则增加在私有代码内。它可能包括补丁级别的更改。修补程序版本当次要版本增加时,必须重置为0。

  8. 如果有任何倒退,主版本X(X.y.z | X>0)必须递增向公共API引入了不兼容的更改。它可能还包括未成年人和补丁级别的更改。补丁和次要版本必须在主要版本时重置为0版本增加。

  9. 预发布版本可以通过附加连字符和紧跟补丁之后的一系列点分隔标识符版本。标识符必须仅包含ASCII字母数字和连字符[0-9A-Za-z-]。标识符不得为空。数字标识符必须不包括前导零。预发布版本具有较低的优先于相关的正常版本。预发布版本指示版本不稳定,可能不满足预期兼容性要求,如相关正常版本。示例:1.0.0-alpha、1.0.0-alfha.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+2013031344700,1.0.0-beta+exp.sha.5114f85,1.0.0+21AF26D3----117B344092BD。

  11. 优先级是指在订购时版本之间的比较方式。

    1. 必须通过将版本划分为主要版本来计算优先级,次要、补丁和预发布标识符的顺序(构建元数据没有优先考虑)。

    2. 优先顺序由比较以下各项时的第一个差异确定这些标识符从左到右如下:主要、次要和补丁版本总是在数字上进行比较。

      例如:1.0.0<2.0.0<2.1.0<2.1.1。

    3. 当主版本、次版本和修补程序相等时,预发布版本具有更低的优先于普通版本:

      例如:1.0.0-alpha<1.0.0。

    4. 具有相同主版本、次版本和补丁版本必须通过比较每个点分隔的标识符来确定从左到右,直到发现差异如下:

      1. 仅由数字组成的标识符进行数字比较。

      2. 带有字母或连字符的标识符在ASCII中进行词汇比较排序顺序。

      3. 数字标识符的优先级始终低于非数字标识符标识符。

      4. 较大的预发布字段集的优先级高于较小的集合,如果前面的所有标识符都相等。

      示例:1.0.0-alpha<1.0.0-alfha.1<1.0.0-alpha.beta<1.0.0-beta<1.0.0-beta.2<1.0.0-besta.11<1.0.0-rc.1<1.0.0。

Backus–有效SemVer版本的Naur形式语法

<有效semver>::=<版本核心>|<版本核心>“-”<预发布>|<版本核心>“+”<内部版本>|<版本核心>“-”<预发布>“+”<内部版本><版本核心>::=<主要>“.”<次要>“.“<补丁><主要>::=<数字标识符><次要>::=<数字标识符><补丁>::=<数字标识符><预发布>::=<点分隔的预发布标识符><点分隔的预发布标识符>::=<预发布标识符>|<预发布标识符>“.”<点分隔的预发布标识符<构建>::=<点分隔的构建标识符><点分隔的构建标识符>::=<构建标识符>|<构建标识符>“.”<点分隔的构建标识符><预发布标识符>::=<字母数字标识符>|<数字标识符><构建标识符>::=<字母数字标识符>|<数字><字母数字标识符>::=<非数字>|<非数字><标识符字符>|<标识符字符><非数字>|<标识符字符><非数字><标识符字符><数字标识符>::=“0”|<正数>|<正数><数字><标识符字符>::=|<标识符字符><标识符字符>::=<数字>|<非数字><非数字>::=<字母>| "-"<数字>::=<数字>|<数字><数字><数字>::=“0”|<正数><正数>::=“1”|“2”|“3”|“4”|“5”|“6”|“7”|“8”|“9”<字母>::=“A”|“B”|“C”|“D”|“E”|“F”|“G”|“H”|“I”|“J”|“K”|“L”|“M”|“N”|“O”|“P”|“Q”|“R”|“S”|“T”|“U”|“V”|“W”|“X”|“Y”|“Z”|“a”|“b”|“c”|“d”|“e”|“f”|“g”|“h”|“i”|“j”|“k”|“l”|“m”|“n”|“o”|“p”|“q”|“r”|“s”|“t”|“u”|“v”|“w”|“x”|“y”|“z”

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

这不是一个新的或革命性的想法。事实上,你可能会做点什么已经接近了。问题是“接近”不够好。没有符合某种形式的规范,版本号是基本上对依赖关系管理没有用处。通过提供名称和明确对上述想法的定义,很容易传达你的意图您的软件用户。一旦这些意图明确、灵活(但不太灵活)可以最终制定依赖性规范。

一个简单的示例将演示语义版本控制如何建立依赖关系地狱般的过去。考虑一个名为“Firetruck”的库。它需要一个语义版本化包名为“Ladder”Ladder的版本为3.1.0。因为Firetruck使用了一些功能在3.1.0中首次介绍,您可以安全地指定Ladder依赖性大于或等于3.1.0但小于4.0.0。现在,什么时候Ladder版本3.1.1和3.2.0可用,您可以将其发布到您的程序包管理系统,并知道它们将与现有的依赖软件。

当然,作为一名负责任的开发人员,您需要验证包升级功能如广告所示。现实世界是一个混乱的地方;对此我们无能为力,只能保持警惕。你能做的就是让语义版本控制为您提供了一种合理的发布和升级方法包,而不必滚动依赖包的新版本,从而节省您的时间时间和麻烦。

如果所有这些听起来都不错,那么开始使用语义版本控制是声明您正在这样做,然后遵循规则。链接从您的自述文件访问此网站,以便其他人了解规则并从中受益他们。

常见问题解答

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

最简单的方法是从0.1.0开始初始开发版本然后为每个后续版本增加次要版本。

我如何知道何时发布1.0.0?

如果您的软件正在生产中使用,它可能已经1.0.0. 如果您有用户依赖的稳定API,您应该为1.0.0。如果你非常担心向后兼容性,你应该可能已经是1.0.0了。

这不妨碍快速开发和快速迭代吗?

主要版本0是关于快速开发的。如果您要更改API每天您都应该使用版本0.y.z或单独的开发部门正在开发下一个主要版本。

如果即使对公共API进行最微小的向后不兼容更改也需要进行重大的版本升级,那么我会很快升级到42.0.0版吗?

这是一个负责任的发展和远见的问题。不相容的对具有大量相关代码。升级所需的成本可能很高。必须修改主要版本才能发布不兼容的更改,这意味着您将思考变革的影响,并评估成本效益比卷入的。

记录整个公共API的工作太繁重了!

作为专业开发人员,您有责任正确记录供他人使用的软件。管理软件复杂性是保持项目效率非常重要,如果没有人知道如何使用你的软件,也没有人知道什么方法可以安全调用。长期来看,语义版本化,以及对定义明确的公共的坚持API可以让每个人和每件事都能顺利运行。

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

一旦你意识到你已经打破了语义版本规范,请修复并发布一个新的次要版本来纠正问题恢复向后兼容性。即使在这种情况下无法修改版本化版本。如果合适的话,记录有问题的版本并将问题告知用户,以便他们知道冒犯的版本。

如果在不更改公共API的情况下更新自己的依赖项,我该怎么办?

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

如果我无意中以不符合版本号更改的方式更改了公共API(即代码错误地引入了补丁版本中的重大突破性更改),该怎么办?

运用你最好的判断力。如果你有大量观众受将行为更改回公共API预期行为的影响,然后最好执行主要版本的发布,即使修复可能严格视为补丁版本。记住,语义版本控制就是全部关于通过版本号如何变化来传达含义。如果这些变化对于用户来说很重要,请使用版本号通知他们。

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

贬低现有功能是软件开发和通常需要取得进步。当你抨击你的公共API,您应该做两件事:(1)更新文档以允许用户知道更改,(2)发布一个新的次要版本到位。在新的主要版本中完全删除功能之前应该至少有一个次要版本包含不推荐的内容,所以用户可以顺利过渡到新API。

SemVer是否对版本字符串有大小限制?

不,但要有良好的判断力。255个字符的版本字符串可能是多余的,例如。此外,特定系统可能会对字符串。

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

不,“v1.2.3”不是语义版本。但是,在语义版本前面添加前缀带有“v”是一种常见的方式(英语),表示它是一个版本号。版本控制中经常会看到将“版本”缩写为“v”。例子:git tag v1.2.3-m“发布版本1.2.3”,在这种情况下,“v1.2.3”是一个标记名称和语义版本为“1.2.3”。

是否有建议的正则表达式(RegEx)来检查SemVer字符串?

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

请参见:https://regex101.com/r/Ly7O1x/3/

^(?P<主要>0|[1-9]\d*)\。(?P<次要>0|[1-9]\d*)\。(?P<补丁>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<构建元数据>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)$

而一个具有编号的捕获组(因此cg1=主要、cg2=次要,cg3=补丁、cg4=预发布和cg5=构建元数据)兼容使用ECMA脚本(JavaScript)、PCRE(Perl兼容正则表达式,即Perl、PHP和R)、Python和Go。

请参见:https://regex101.com/r/vkijKf/1/

^(0|[1-9]\d*)\。(0|[1-9]\d*)\。(0|[1-9]\d*)(?:-((?:0|[1-8]\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联合创始人。

如果你想留下反馈,请在上打开问题github.

许可证

创意共享——CC BY 3.0