SemVer规范与npm版本管理

1,364 阅读6分钟

一、介绍

1、SemVer规范

语义化版本控制规范(SemVer)由 Github 起草的一个具有指导意义的,统一的版本号表示规则。 npm包中的模块版本需要遵循SemVer规范。

SemVer规范官网:语义化版本 2.0.0

1.1 规范摘要

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  • 主版本号:当你做了不兼容的 API 修改,
  • 次版本号:当你做了向下兼容的功能性新增,
  • 修订号:当你做了向下兼容的问题修正。
  • 先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

1.2 简介

在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

SemVer建议用一组简单的规则及条件来约束版本号的配置和增长。使用这样的版本号格式:X.Y.Z(主版本号.次版本号.修订号)修复问题,递增修订号;保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。

这套系统被称为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。

1.3 语义化版本规范

  • 使用语义化版本控制的软件必须(MUST)定义公共 API。该 API 可以在代码中被定义或出现于严谨的文档内。无论何种形式都应该力求精确且完整。

  • 标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。

  • 标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。

  • 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。

  • 1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。

  • 修订号 Z(x.y.Z | x > 0)必须(MUST)在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。

  • 次版本号 Y(x.Y.z | x > 0)必须(MUST)在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也必须(MUST)递增。也可以(MAY)在内部程序有大量新功能或改进被加入时递增,其中可以(MAY)包括修订级别的改变。每当次版本号递增时,修订号必须(MUST)归零。

  • 主版本号 X(X.y.z | X > 0)必须(MUST)在有任何不兼容的修改被加入公共 API 时递增。其中可以(MAY)包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号必须(MUST)归零。

  • 先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。

  • 版本编译信息可以(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。

1.4 版本的优先层级规则

  • 判断优先层级时,必须(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。

1.5 FAQ

  • 版本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高低排序?

  • 在 0.y.z 初始开发阶段,我该如何进行版本控制?

    最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。

  • 如何判断发布 1.0.0 版本的时机?

    当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。

  • 万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办?

    一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文档中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。

  • 如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢?(意即在修订等级的发布中,误将重大且不兼容的改变加到代码之中)

    自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。

二、NPM包版本管理

1、查看npm包版本

npm view package version //查看某个package的最新版本

npm view package versions // 查看某个包在npm服务器上所有发布过的版本

2、npm包版本管理

2.1、标准版本

SemVer规范的标准版本号采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须以数值来递增。

  • 主版本号(major):当你做了不兼容的API 修改
  • 次版本号(minor):当你做了向下兼容的功能性新增
  • 修订号(patch):当你做了向下兼容的问题修正。

比如:2.1.0 > 1.12.2 > 1.10.12

2.2、先行版本

当某个版本改动比较大、并非稳定而且可能无法满足预期的兼容性需求时,你可能要先发布一个先行版本。

先行版本号可以加到“主版本号.次版本号.修订号”的后面,先加上一个连接号“-”再加上一连串以句点分隔的标识符和版本编译信息。

  • 内部版本(alpha):如1.0.0-alpha.1
  • 公测版本(beta):如1.0.0-beta.1
  • 正式版本的候选版本rc: 即 Release candiate,如1.0.0-rc.1
npm view vue versions

企业微信截图_16666856783612.png

vue的版本是遵循了SemVer规范。

  • 版本号严格按照 主版本号.次版本号.修订号 格式命名
  • 版本是严格递增的
  • 发布重大版本或版本改动较大时,先发布alpha、beta、rc等先行版本

2.3 管理版本

在修改 npm 包某些功能后通常需要发布一个新的版本,我们通常的做法是直接去修改 package.json 到指定版本。如果操作失误,很容易造成版本号混乱,我们可以借助符合 Semver 规范的命令来完成这一操作:

命令作用执行结果version
npm version prerelease升级预发布号首次执行1.0.0 -> 1.0.0-0 再次执行1.0.0 -> 1.0.0-1
npm version prepatch升级修订号,保留预发布号v1.0.1-1-> v1.0.2-0
npm version preminor升级次版本号,保留预发布号v1.0.2-0-> v1.1.0-0
npm version premajor升级主版本号,保留预发布号v1.1.0-0-> v2.0.0-0
npm version patch升级修订号,不保留预发布号v2.0.0-0-> v2.0.0 -> v2.0.1
npm version minor升级次版本号,不保留预发布号v2.0.1-> v2.1.0
npm version major升级主版本号,不保留预发布号v2.1.0-> v3.0.0

2.4 依赖版本管理

在 package.json 中各种依赖的不同写法:

	"dependencies": {
		"signale": "1.4.0", // 固定版本号
		"figlet": "*", // 任意版本(>=0.0.0)
		"vue": "2.x", // 匹配主要版本(>=2.0.0 <3.0.0)
		"table": "~5.4.6", 
		"yargs": "^14.0.0"
	},

再来看看后面两个,版本号中引用了 ~ 和 ^ 符号:

  • ~: 当安装依赖时获取到有新版本时,安装到 x.y.z 中 z 的最新的版本。即保持主版本号、次版本号不变的情况下,保持修订号的最新版本。

  • ^: 当安装依赖时获取到有新版本时,安装到 x.y.z 中 y 和 z 都为最新版本。即保持主版本号不变的情况下,保持次版本号、修订版本号为最新版本。

当主版本号为 0 的情况,会被认为是一个不稳定版本,情况与上面不同:

  • 主版本号和次版本号都为 0: ^0.0.z、~0.0.z 都被当作固定版本,安装依赖时均不会发生变化。
  • 主版本号为 0: ^0.y.z 表现和 ~0.y.z 相同,只保持修订号为最新版本

1.0.0 的版本号用于界定公共 API。当你的软件发布到了正式环境,或者有稳定的API时,就可以发布1.0.0版本了。所以,当你决定对外部发布一个正式版本的npm包时,把它的版本标为1.0.0。

参考

剖析npm包版本管理机制