原文链接:http://semver.org/

在软件管理的世界里有一个可怕的地方叫“依赖地狱(dependency hell)”。你的系统越是成长壮大,你越是整合更多的软件包到你自己的系统中,你越有可能在将来的某天发现自己已经掉进了这个绝望的深渊。

在一个有着众多依赖的系统里,发布新版本可能很快成为一个恶梦。如果依赖定义得过于紧密,你就有可能进入版本锁定(version lock)的状态(版本锁定是指一旦更新一个软件包,就不得不更新其他所有依赖于它的包)。如果依赖定义得过于松散,你又难免会被版本穿插(version promiscuity)所伤(让人以为会与多得不合理的未来版本兼容)。当你被版本锁定或版本穿插所阻挠而不能容易地让你的项目顺利前进时,你就身处依赖地狱中了。

作为这个问题的解决方案之一,我提议用一组简单的规则和要求来约束版本号的分配和增长规则。为了让这套理论运作,你必须预先定义好自己的公共API。这可以通过文档定义或代码强制要求来实现。无论如何,这套API的清楚明了是十分重要的。一旦你定义了公共API,你就可以通过修改相应的版本号来通知大家你的修改。考虑使用这样的版本号格式:X.Y.Z(主版本号,次版本号,补丁版本号)修复Bug但不影响API时增长补丁版本号;API保持向下兼容的增加/修改时增长次版本号;进行不向下兼容的修改时增长主版本号。

我把这套规则称为“语义版本命名(Semantic Versioning)”。在这套工作模式下,版本号和它们的增长模式就会传达从当前版本向下一个版本进行了怎样的修改。

语义版本(SemVer)命名规范

在篇文章里出现的关键字“必须”,“必须不”,“要求”,“应该”,“不应该”,“一定要”,“一定不要”,“推荐”,“可以”和“可选”将在RFC2119中描述和解释。(以下译文中原样使用这些关键字看上去会比较生硬,但为了清楚地传达作者的意图和保持RFC2119关键字的意义,仍然照这里的翻译来使用——译者注)

  1. 使用语义版本命名的软件系统必须定义一套公共API。这套API可以是在代码中申明或是用严格的文档定义。不管怎样做,它都应该清楚明了。
  2. 正常的版本号必须使用X.Y.Z的形式并且X/Y/Z是非负整数。X是主版本号,Y是次版本号,Z是补丁版本号。版本号每次必须只能增长1。例如:1.9.0->1.10.0->1.11.0。
  3. 当主版本号增长时,次版本号和补丁版本号必须清零。当次版本号增长时,补丁版本号必须清零。例如:1.1.9->2.0.0,2.1.7->2.2.0。
  4. 一旦发布了具有版本的包,那个版本的内容必须不能再更改。任何修改必须发布成一个新版本。
  5. 主版本号0 (0.y.z)是用来进行初始开发时使用的。任何东西都可能在任何时候改变。公共API此时应该被认为是经常变动的。
  6. 版本1.0.0开始定义公共API。这个版本及以后的版本号的增长方式将依赖于公共API以及它如何变化。
  7. 如果有任何向下兼容的bug修复发生,补丁版本号Z (x.y.Z | x > 0)必须增长1。“bug修复”被定义为内部进行的修复非正常行为的修复工作。
  8. 如果进行了新的并且向下兼容的公共API添加和修改,次版本号Y (x.Y.z | x > 0)必须增长1。如果任何公共API被标记为“过期”,次版本号必须增长1;如果有大量的新功能或改进在内部代码中发生,次版本号可以增长1;这其中也可以包含补丁级别的修改。当次版本号增长时补丁版本号必须清零。
  9. 如果对公共API有任何向下不兼容的修改,主版本号X (X.y.z | X > 0)必须增长1。这其中也可以包含次版本和补丁版本级别的修改。当主版本号增长时次版本号和补丁版本号必须清零。
  10. 预览版本(pre-release version)可以通过在补丁版本号后追加中横线以及由点分隔开的一系列标识来表达。标识必须由ASCII字符和中横线[0-9A-Za-z-]组成。预览版本能满足相关版本的要求,但优先级低于相关版本。例如:1.0.0-alpha,1.0.0-alpha.2,1.0.0-0.3.7,1.0.0-x.7.z.92。
  11. 构建版本(build version)可以通过在补丁版本号或预览版本后追加一个加号和一系列由点分隔标识来表达。标识必须由ASCII字符和中横线[0-9A-Za-z-]组成。构建版本能够满足相关版本的要求,并且优先于相关版本。例如:1.0.0+build.1,1.3.7+build.11.e0f985a。
  12. 将版本号分为主版本号、次版本号、补丁版本号,预览版本,构建版本,必须按这样的按顺序分别逐级考虑来确定版本顺序。主版本号、次版本号,补丁版本号总是通过数字大小来确定顺序。预览版本和构建版本的顺序必须由比较由点分隔标识来确定,规则如下:如果标识只有数字,则由数字大小决定;如果标识包含字符和中横线,则由比较字符的字典顺序来确定。数字标识的顺序永远低于非数字标识。例如:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0-rc.1+build.1 < 1.0.0 < 1.0.0+0.3.7 < 1.3.7+build < 1.3.7+build.2.b8f12d7 < 1.3.7+build.11.e0f985a。

为什么使用语义版本命名?

这并不是一个全新的革命性的想法。事实上,你可能已经做了和这差不多的事情了。问题是“差不多”还不够好。如果不服从某种正式的规范,版本号对于版本依赖管理就失去了本质上的意义。通过给以上的想法一个清楚的定义和命名,与你的软件用户沟通你的意图就变得容易了。一旦这些意图明确表达出来,灵活(但不是过于灵活)的依赖定义就可以最终被制定出来。

一个简单的例子可以演示语义版本命名如何让版本地狱成为过去。考虑有一个库叫做“救火车”。它需要一个语义版本命名的包“云梯”。当救火车被制造的时候,云梯的版本是3.1.0。因为救火车一开始使用了由云梯3.1.0提供的某些功能,你可以安全地知道对云梯的正确依赖是在3.1.0以后并且4.0.0之前。现在,当云梯版本3.1.1和3.2.0发布时,你就可以把它们放到你的软件包管理系统中并且知道它们会与依赖它的软件兼容。

作为一个有责任的开发人员,你当然一定会想要确保所有软件包更新都被广而告之。现实世界是一个混乱的地方,除了提高警惕我们别无他法。你所能做的是让语义版本命名为你提供一个健全的方式来发布版本更新软件包,而不必更新所有的依赖软件包,这将会节省你的时间,少为你添麻烦。

如果这些听起来让你满意的话,你所需要对语义版本命名做的事情就是:申明你正在使用它并且按它的要求办事。在你的README文档中链接到这个网站,让其他人知道这些规则并且从中受益。

FAQ

【内容太多,过两天再补】

关于

语义版本命名规范由Tom Preston-Werner提出,他是Gravatars的创始人和GitHub的合作创始人。

如果你想留下一些反馈,请在GitHub提交一个新的问题

License

Creative Commons - CC BY 3.0 http://creativecommons.org/licenses/by/3.0/

posted on 2012-05-14 01:56  yaoxing  阅读(3694)  评论(4编辑  收藏  举报