发 npm 包对于稍微大点的厂来讲都是个频繁需求,所以本地执行计算版本命令以及 npm publish
都是不大可行的,大都会有一个单独的部署系统去自动帮助咱们完成这个事情。git
今天就来聊聊这个部署系统中核心的计算版本以及发布的逻辑以及流程。github
聊部署系统以前,咱们先得来聊聊语义化版本,由于笔者发现不少人对于这一块内容仍是只知其一;不知其二。正则表达式
版本中携带 alpha、beta、rc 等 tag 字样,统称先行版,通常格式为 x.y.z-[tag].[次数 / meta 信息]npm
通常的版本号格式都为 X.Y.Z,分别的含义为:json
可是这个语义也不是一成不变的。好比当版本号为测试版时(版本小于 1.0.0 时),咱们能够将语义修改成 0.minor.patch
。由于此时出现不兼容 API 是很正常的事情,不该该直接改动主版本号,而是应该改动次版本号,同时将功能新增及 bug 修复形成的版本变动体如今补丁上。bash
X.Y.Z 必须为正整数且前面不能补零。frontend
X.Y.Z 在每次变动版本号时,须要重置更小的版本号至 0。好比说 1.0.2 升级至 1.1.0。工具
同一个版本的先行版屡次发布,只需变动末尾的次数或者 meta 值。好比说 1.0.0-beta.0 再次发布先行版应为 1.0.0-beta.1。测试
测试版通常从 0.1.0 开始计算。正式版通常在结束快速迭代以及开发者认为 API 稳定之后就能够发布。spa
npm 项目分为两种包结构:
若是咱们须要实现自动发版,就得让服务知道咱们究竟是须要发什么版本,不然不管怎样都不可能实现自动计算版本的需求。
所以咱们须要引入 commitizen 这个工具。
这个工具能够帮忙咱们提交规范化的 commit 信息:
格式以下:
<type>[optional scope]: <description> [optional body] [optional footer(s)]
通常对于平常开发来讲没那么严谨,type、description 必填,breaking 在须要的时候选择,其余内容无关紧要。
未免读者不明白这三者分别表明什么,笔者先来解释下
type 就是本次 commit 所作的代码变更,基本分为如下几种:
固然若是用户有个性化需求的话,也是能够增删这部份内容的。
description 就是字面意思了,表明本地 commit 的信息。
最后是 breaking,当有不兼容的 API 出现时咱们须要提交这部分的内容,告知正式版须要变动主版本号。
PS:上文中说的都是正式版,若是当前版本为测试版的话,变动版本规则参考 版本变动规则小节。
最后生成出来的内容大体长这个样子:fix: do not alter attributes
。
另外还有一个注意点是多包的问题。若是发版用统一版本的话事情就基本回归到单包结构上了,很简单。可是若是版本不统一的话,咱们就须要收集到底有哪些包是变动过文件的,在部署时只对变动过的包进行操做。
这里大体有两种方法能够实现:
第一种方法是 @lerna/changed,这个工具能够帮助咱们找到全部变动过的二方包。这里原理其实挺简单的,核心就是经过 git command 去实现:
git diff --name-only {git tag / commit sha} --{package path}
翻译过来就是寻找从上次的 git tag 或者初次的 commit 信息中查找某个包是否存在文件变动。
第二种方式能够改造 git cz 工具,新增一个功能:每次提交的时候自动带上本次提交变动了哪些包,固然底下仍是用了上面的原理,可是咱们能够根据需求来定制更多的功能,更自由。
规范化的 commit 信息是自动部署系统的基石,不管用工具也好仍是直接手写,同时也能让开发者清晰地了解大体项目作了哪些变动。
计算版本是个挺有趣的东西,这里咱们须要用到 semver 来帮助咱们计算,固然多包场景下你也可使用 lerna 计算,可是咱们内部仍是直接统一都 semver 算了。
另外算版本这件事情须要分场景来论,接下来咱们一个个来看。
固然在开始以前,咱们须要了解下版本的通用变动规则,由于几个场景都基于这个通用规则。
首先来介绍下升版本所须要用到的几种类型:
major | minor | patch | premajor | preminor | prepatch | prerelease
前三种以前就聊过,这里再也不多说。
以后三种都对应先行版,以 beta 版本举例 premajor,能将版本 1.0.0 变动为 2.0.0-beta.0,其实大致上仍是和前三者相同,无非多了一个先行版本号。
最后种一样也是对应先行版。以 beta 版本举例,能将版本 1.0.0 变动为 1.0.1-beta.0,同时也能将版本 1.0.1-beta.0 变动为 1.0.1-beta.1。
知道了变动版本类型,咱们就该想该如何得出它们了。通常来讲用户都会提交多个 commit,咱们首先须要找出其中全部的 commit type 而且取出一个最大值。
好比说用户自上次发版以来共提交了三个 commit,类型分别为 feat、doc、breakchange,那么最大类型为 breakchange。
得出最大 commit type 后,咱们须要根据不一样的版原本计算。好比说正式版与测试版发版规则就不一样,详见 版本号格式,再也不赘述。
举个例子,当前版本为 1.0.0,此时根据 commit 咱们得出最大 type 为 feat,且须要发布先行版(beta),所以最终计算得出的变动规则为 preminor,可将版本升级为 1.1.0-beta.0。
上文有说到咱们须要分析 commit 来获取 type,那么读者可能会疑问如何分析?
其实原理很简单,仍是用到了 git command:
git log -E --format=%H=%B
对于以上 commit,咱们能够经过执行命令得出如下结果:
固然这样分析是把当前分支的全部 commit 都分析进去了,大部分发版时候咱们只须要分析上次发版至今的全部变动,所以须要修正 command 为:
git log 上次的 commit id...HEAD -E --format=%H=%B
最后就是各类正则表达式大显身手的时候了,想拿啥信息匹配就行。
单包场景实际上是最简单的,直接套用通用变动逻辑就行。
单纯的多包环境其实也是简单的。无非比单包多了一步须要先找到哪些文件被变动了,而后须要筛选出 commit 中对应当前包的 type,最后就是套用通用变动逻辑。
先解释下这个场景,好比说目前维护了 A、B、C 三个包,A 包的 dependencies 中包含了 B 和 C,这就是有依赖的意思,此时计算版本还须要多个步骤。
当通用逻辑结束之后,咱们须要根据依赖关系来判断是否还须要变动包版本。
好比说本地提交须要执行 patch 变动 B 包版本,其它两包都没有代码变更。可是实际上咱们还须要变更 A 的版本,不然光升 B 不升 A,用 A 包的用户就用不到 B 包的新版本变化了。
当咱们将全部版本计算完毕之后,就须要写入 package.json 而后 执行 npm publish
,最后还须要提交下代码打上 tag。
本篇文章就是大体聊了下算版本以及发布这部分的内容,你们有问题的能够交流讨论。
另外我相信确定有读者会说这作的太麻烦,有别的工具能够简化步骤。这个笔者固然也知道,可是他们底下自动修改版本的原理都是和本文一致的,了解下工具底下是怎么作事的也不为过。