语义版本号(Semantic Versioning)


本文将推荐一种新的版本号格式——语义版本号,格式类似这样 1.4.6-beta

【传统的版本号】

    如果你只是知道传统版本号由四个部分组成,那么建议去官方文档 Assembly Versioning 了解一下这种版本号的定义。

    它分为 主版本号.次版本号.构建号.修订号 四个部分,但是后面的一个或多个部分可以省略。

    例如,1.5.1254.0 表示主版本号是 1,次版本号是 5;在 1.5 的版本下,第 1255 次构建,并且在这次构建之后没有进行修订。如果你是一个库的发布者,那么主版本号的改变意味着 API 出现不兼容的修改;次版本号改变意味着 API 出现兼容的修改(通常是新增)。

    然而我们如何能够准确地向所有人传递这样的版本规则呢?当我们在向全世界提供一个库(比如 NuGet 包)的时候,我们怎么让团队所有人都知道我们正在为哪个版本开发新功能呢?我们又应该在何时更新程序集或者 NuGet 的版本号呢(在功能开发开始?差不多完成?临近发布?)?

    传统的版本号记录不了这些信息,于是我们不得不用一些额外的方式来记录,这就增加了维护成本。


【语义版本号】

    语义版本号由五个部分组成 主版本号、次版本号、补丁号、预发布版本标签 和 构建号。

    举例看看语义版本号是什么样的吧(摘自 NuGet Package Version Reference):

    ·    1.0.1

    ·    1.0.1-rc

    ·    1.0.1-beta

    ·    1.0.1-alpha2

    ·    1.0.1-alpha

    ·    1.0.1-aaa


【规范】

    使用 Semantic Versioning 的软件 必须 声明一个公共的 API. 这个 API 可能是定义在代码里的, 或者仅仅存在于文档里, 不论用什么方式实现, 它都必须精确而全面.

    一个正常的版本号必须使用 X.Y.Z 的格式, 其中 X, Y, 和 Z 都是非负的整数, 并且 必须不能 包含前导零。

    ·    X 是主版本号, Y 是次版本号, 而 Z 是补丁版本号. 每个元素都必须以数字的方式递增. 举例: 1.9.0 -> 1.10.0 -> 1.11.0.

    一旦一个打了版本的包被发布出去了, 那个版本的内容就 不能 再修改了. 任何修改 必须 作为一个新的版本重新发布.

    主版本为零 (0.y.z) 的版本, 是用作初始开发阶段的. 任何东西都可能在任意的时间被更改. 这时候我们不应该认为它的 API 是稳定的.

    1.0.0 版本表明对外公开 API 的形成. 从此之后, 版本号的递增方式取决于这个公开的API, 以及它如何修订.

    补丁版本号Z (x.y.Z | x > 0) . 如果只有向后兼容的bug修复被引入的化, 补丁版本号 Z 必须 递增. "Bug修复"是指一个修正错误行为的内部修改.

    次版本号Y (x.Y.z | x > 0). 如果一个新的, 向后兼容的功能被引入到了公开 API 里, 次版本号 必须 递增. 如果公开 API 的任何功能被标记为 "已弃用的", 次版本号 必须 递增. 如果大量的新功能或改进被引入到私有代码里的时候, 次版本号 可以 递增. 次版本号的改变 可以 包含补丁级别的改动. 当递增了次版本号的时候, 补丁版本号 必须 清零.

    主版本号X (X.y.z | X > 0). 如果任何的向后不兼容的改动被引入到了公开 API中, 主版本号 必须 递增. 它的递增 可以 包含次版本和补丁级的改动. 当主版本号递增时, 次版本号和补丁版本号 必须 清零.

    一个预发布版本 可以 通过在补丁版本号后面追加一个短线, 以及一系列的用点分割的标识符 来描述. 标识符 必须 仅包含 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.

    编译时的附加信息, 可以 通过在补丁版本号后面追加一个加号, 以及一系列的用点分割的标识符 来描述. 标识符 必须 仅包含 ASCII 的 阿拉伯数字和短线 [0-9A-Za-z-]. 标识符 必须不 为空. 在比较版本优先级的时候, 编译附加信息 应该 被忽略. 因此, 两个只有编译附加信息不同的版本, 具有相同的优先级. 编译附加信息的举例: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.

    优先级是指在排序的时候怎样比较不同的版本. 计算优先级的时候, 必须 将版本号以 "主版本号", "次版本号", "补丁版本号", "预发布标识符" 的顺序拆分. 优先级取决于, 在从左至右依次比较这些个标识符的时候, 发现的第一个差别. "主版本号", "次版本号", "补丁版本号" 总是以数字的方式参加比较. 举例: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1.

    当"主版本号", "次版本号", "补丁版本号" 都相同的时候, 预发布版本比正常的版本优先级要低. 举例: 1.0.0-alpha < 1.0.0.

    ·    如果两个预发布版本有相同的 "主版本号", "次版本号", "补丁版本号", 优先级就 必须 通过比较点分割的标识符来确定, 从左至右依次比较, 直到发现一个不同: 只有数字的标识符号以数值高低比较, 有字母或连接号时则逐字以 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.


【为什么要用语义版本号】

    我们可以通过一个简单的例子,展示一下 Semantic Versioning 可以让 “Dependency Hell” 成为历史。考虑一个叫做 “消防车” 的版本库,他需要一个使用了 Semantically Version 的软件包 “梯子”。当 “消防车” 刚创建的时候,“梯子”在3.1.0 版本。因为“消防车”使用了 “梯子” 3.1.0 新引入的功能,你可以简单的指定,你的“消防车” 对“梯子”的依赖要求是:大于等于 3.1.0 ,但小于 4.0.0 。现在,梯子 "3.1.1" 和 "3.2.0" 好了,你可以把他们发布到你的软件包管理系统,并且你知道他们跟现有的、跟它有依赖关系的包是兼容的。

    作为一个负责人的开发者,你当然想验证任何软件包升级都是正常的、跟你宣传的一样。但现实世界是一团乱麻,对此我们除了小心再小心之外没有更好的办法。你能做的,是让 “Semantic Versioning” 提供你一个合理的方法去发布、升级软件包,而不必去搞许多新版的依赖包,节省了你的时间,免去了许多麻烦。

    如果所有这些听起来挺给力的,要开始使用 “Semantic Versioning” 的话,你只需要声明你正在这么搞,然后遵守这些规范就好了。把这个网站链接到你的 README 文档里,以便其他其他人也可以了解这些规则,并从中受益。


上一篇 下一篇

评论

登录后可发表评论