共计 8263 个字符,预计需要花费 21 分钟才能阅读完成。
1. 传统的发包模式
1.1 版本公布
传统的发包模式指用户在本地进行发包、版本升级的操作,因而所有的 cli 都是在本地执行。当咱们写好一个 npm package 之后,并且登录好 npm 后,就能够执行以下指令间接公布第一版:
npm publish --access public
1.2 版本迭代
当进行了一些变更之后,能够手动去变更 package.json
的版本号,当然这是一种十分低效且不优雅的做法,手动变更版本号存在太多不确定的因素,比方改错版本号或跳过某个版本号;同时,个别咱们在生成一个新的版本后要打一个 tag 对以后版本进行留档,纯手动操作的话会有很多的工作量。因而咱们须要一个更加『靠谱』的迭代版本号的计划。
1.2.1 npm 版本标准
在议论如何优雅的进行迭代版本号之前,咱们先来理解一下 npm 采纳的语义化版本号:
npm 的语义化版本,共三位,以’.’隔开,从左至右顺次代表:
- 主版本(major)
- 主要版本(minor)
- 补丁版本(patch)
举例来说:1(major).0(minor).0(patch)
当然有时某些包还存在预览版本,预览版本的版本号要与前三位版本号应用 -
进行距离,如:
- 1.0.0-1
- 1.0.0-alpha.1
- 1.0.0-beta.1
- 1.0.0-rc.1
alhpa
/beta
/rc
这些并不是 npm 官网定义的 prerelease 前缀,你能够应用任何前缀,甚至niconiconi
,如何增加这些前缀,咱们前面会探讨到。
对于版本变更的标准,举荐采纳以下策略:
代码状态 | 等级 | 规定 | 版本样例 |
---|---|---|---|
首次公布 | 新品公布 | 以 1.0.0 开始 | 1.0.0 |
bug 修复,向后兼容 | 补丁版本公布 | 变更第三位数字 | 1.0.1 |
新性能,向后兼容 | 次版本公布 | 变更第二位数字,并且第三位数字重置为 0 | 1.1.0 |
重大变更,不向后兼容 | 主版本公布 | 变更第一位数字,并且第二位和第三位数字重置为 0 | 2.0.0 |
1.2.2 应用 npm version 变更版本号
npm 提供了 npm version
指令能够辅助咱们来进行版本迭代,假如咱们当初的版本是 1.0.0
,应用 npm version
的各个参数进行版本升级,失去的后果如下:
对于个别的迭代,应用 major
/ minor
/ patch
即可:
- npm version major => 2.0.0
- npm version minor => 1.1.0
- npm version patch => 1.0.1
如果你要公布预览版本(prerelease)的 package,你能够应用 premajor
/ preminor
/ prepatch
并联合 prerelease
来降级预览版本号:
-
npm version premajor => 2.0.0-0
发型一版重大变更预览版本的 package
-
npm version prerelease => 2.0.0-1
减少以后预览版本的版本号
- npm version major => 2.0.0
正式公布
- npm version major => 2.0.0
-
-
npm version preminor => 1.1.0-0
-
npm version prerelease => 1.1.0-1
- npm version minor => 1.1.0
-
-
npm version prepatch => 1.0.1-0
-
npm version prerelease => 1.0.1-1
- npm version patch => 1.0.1
-
如果你想为预览版的版本号增加 alpha
/ beta
这样的前缀的话,能够应用 --preid
参数,咱们仍旧以 1.0.0
为初始版本,应用 npm version
进行预览版的版本变更示例如下:
-
npm version prepatch –preid alpha => 1.0.1-alpha.0
-
npm version prerelease => 1.0.1-alpha.1
- npm version patch –preid alpha => 1.0.1
⚠️ 如果要公布以后 preid 的正式版,执行正式版并公布指令时须要后缀 --preid 参数
- npm version patch => 1.0.2
如果不后缀就会间接迭代到下一版本
- npm version prepatch –preid beta => 1.0.2-beta.1
如果切换了 preid 就会从新生成一个新版本,而不是在以后版本迭代版本号
- npm version patch –preid alpha => 1.0.1
-
须要留神的是,当你执行 npm version
指令时,以后的工作区必须是洁净的 ,否则会执行失败;且当执行胜利后,会主动生成一个 commit(commit message 默认为版本号), 同时在这次主动生成的 commit 上打一个 tag,tag 名称即为以 v
结尾的版本号名称,如果你想批改默认的 commit message,你能够应用如下指令:
npm version patch -m "Release version %s" #『%s』代表以后版本号
此外,对于你公布的 prerelease 版本的 package 须要留神以下两点:
- 当用户进行首次装置你的包时,且此时你的包最新的版本为一个 prerelease 版本,那么用户就会装置这个 prerelease 版本;如果用户只想装置稳定版,那么能够通过
npm install xxx@version
,比方npm install xxx@1
或npm install xxx@">1.0.0"
这样的指令装置的包不会装置到 prerelease 版本。 - 然而,当用户以后装置的是一个正式版本的包时,应用
npm update
去更新你的包,是不会被动更新到 prerelease 版本的;但如果正式版用户想要降级为 prerelease 版,能够通过执行npm install package@latest
来装置最新的版本(蕴含预览版)。
1.3 CHANGELOG 的生成
在一些我的项目中,会用 CHANGELOG.md
来标注每个版本的变更内容,这个文件通常是应用专门的工具生成的,比方 conventional-changelog,然而主动生成的条件必须满足:
- 应用规范的 commit 标准,通常在默认状况下应用 Angular 的提交标准,这样
conventional-changelog
就会晓得你每次提交做了什么,是新增了一个 fetature,还是修复了一些 bug,亦或是其余。你能够应用@commitlint/cli
+husky
对你的代码进行提交查看,同时也能够应用commitizen
来生成标准化的 commit,对于这些,你能够参考这篇文章。 - 在每次生成一个新的版本后,在以后的提交上要创立一个 tag,tag 的名称为版本号,比方
v1.0.0
,这点如果你应用npm version
来生成版本号的话就无需放心这一点。
一个规范的 commit 历史如下:
commit xxxxxxx (tag: v1.1.0)
Author xxx
Date xxx
1.1.0
commit xxxxxxx
Author xxx
Date xxx
fix: fix a bug
commit xxxxxxx
Author xxx
Date xxx
feat: add new fetaure 2
commit xxxxxxx
Author xxx
Date xxx
feat: add new fetaure 1
commit xxxxxxx (tag: v1.0.1)
Author xxx
Date xxx
1.0.1
commit xxxxxxx
Author xxx
Date xxx
fix: fix a bug
commit xxxxxxx (tag: v1.0.0)
Author xxx
Date xxx
1.0.0
commit xxxxxxx
Author xxx
Date xxx
feat: base function
commit xxxxxxx
Author xxx
Date xxx
chore: first commit
conventional-changelog
读取到这样的 commit 历史后,就能够生成如下的 CHANGELOG:
## 1.0.1 (2022-xx-xx)
### Bug Fixes
* fix a bug
### Features
* add new fetaure 1
* add new fetaure 2
## 1.0.1 (2022-xx-xx)
### Bug Fixes
* fix a bug
## 1.0.0 (2022-xx-xx)
### Features
* base function
如果你的 commit 合乎以上两点要求,你能够装置 conventional-changelog-cli
:
npm install conventional-changelog-cli -D
运行 cli 指令生成 CHANGELOG:
npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0
之后版本变更后想生成新的 CHANGELOG 就只须要再执行一遍下面的指令即可。然而还有一种更简便的形式,就是应用 npm 的 version
钩子来在更新版本号时候主动触发 CHANGELOG 生成,只须要在 package.json
中增加以下 script:
"scripts": {"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"}
增加之后,在咱们执行 npm version
时,一旦版本号变更胜利就会触发 version
script 生成 CHANGELOG,并将生成的 CHANGELOG.md
增加到暂存区,而后 npm version
继续执行,暂存区的代码进行提交,并创立一个 tag。
总之,将所有的流程配置好之后,残缺的工作流如下:
- 编辑代码,增加新性能或者修复 bug;
- 实现某个性能后进行 commit,commit 要合乎 Angular 的提交标准;
- 持续实现其余的性能,并每实现一个性能后及时提交标准化的 commit,直到你想要发版为止;
- 执行
npm version xxx
生成新的版本号,这时 CHANGELOG 和版本号都会主动进行迭代; - 执行
npm publish --access public
进行版本公布。
2. standard-version
standard-version 是 conventional-changelog 举荐应用的标准化 npm 版本生成工具,它能够取代 npm version
指令,并提供更简便、语义化的调用形式;
同时,它也集成了 conventional-chagelog,在生成版本号时会主动创立 CHANGELOG,能够省去咱们本人配置 conventional-chagelog-cli 的过程;
此外它还提供了配置文件,你能够很不便的自定义 CHANGELOG 的输入。
2.1 装置
standard-version 能够装置到全局来代替 npm version
指令,但最好还是装置到本地我的项目中,不便其余开发人员应用,能够用 npx
指令来执行它:
npm install standard-version -D
2.2 应用
应用 standard-version 的前提还是要有标准化的 commit 链,就像下面咱们在 CHANGELOG 生成中所形容的那样。当你实现了一系列的代码变更后,就能够执行 npx standard-version
来生成一个版本(如果是首次公布则须要执行 npx standard-version --first-release
),执行之后 standard-version 会做如下的事件:
- 读取
package.json
查问以后包的版本号,如果没有查问到,就将最初一个 tag 的版本号视作以后的版本号; - 根据你的 commit 信息,来决定下一个版本号(这一过程被称为
bump version
),而后批改package.json
、package-lock.json
等须要迭代版本号的文件中的版本号字段; - 根据你的 commit 信息生成或更新
CHANGELOG.md
文件; - 应用新的版本号为名称,创立一个 tag 进行留档。
在应用 npx standard-version
来迭代版本时,你无需关怀是迭代 major、minor、patch 位的版本号,standard-version
会主动依据你的版本号来决定下一个版本号须要迭代哪一位,比方:当发现自上次版本号生成以来,提交的代码中的 commmit message 中仅有 fix
类型的提交,那么就只会迭代 patch 位的版本号;但如果发现自上次生成版本号以来,携带有 feat
类型的提交,那么就会去迭代 minor 位的版本号。
当然你也能够强行指定降级哪一位版本号,比方:
npx standard-version --release-as minor
npx standard-version --release-as patch
npx standard-version --release-as major
亦或是你想要迭代版本号为 prerelease 版本,那么就要应用上面的指令:
npx standard-version --prerelease alpha
3. semantic-release
目前 standard-version 这个我的项目曾经被标记为 deprecated,意味着后续不再保护,同时也意味着这种模式下的包治理形式正在逐步『落后』。standard-version 在官网文档中也指明了两条前途,如果你是 github 用户的话,作者举荐应用 release-please 进行代替;同时,作者也在文中提到了 semantic-release,它也是一个语义化的 npm 包版本治理计划。
不论是 release-please 还是 semantic-release 也好,他们都解决了 standard-version 的一个痛点,那就是 standard-version 的工作流基于本地,开发人员须要本地进行版本迭代、npm 公布的行为。然而因为 CICD 的风行,仿佛在 CI 上进行 npm 包的版本迭代与公布更为适合,这样就不会造成多个开发人员并行开发时版本抵触的问题了。release-please 与 semantic-release 的目标都是将人为干涉的版本迭代和发包行为,转移到标准化的、可继续的 CI 平台上实现。
机器永远比人类靠谱
鉴于 semantic-release 比 release-please 失去的关注更多,因而咱们重点探讨 semantic-release。
3.1 装置与应用
semantic-release 也提供了 cli 工具,能够疾速的帮你实现装置和配置工作,以 Github 为例,你只须要进入到你的我的项目目录下,输出:
npx semantic-release-cli setup
交互式命令行就会询问你的 NPM 账号、明码、二步验证,从而获取你的 NPM token(用于在流水线发包);之后会疏导你进行 Github 受权,目标是为了在你的指标我的项目的 Secrets 中写入 NPM_TOKEN
的环境变量。
之后你就能够创立你的 Github Action 来轻松的进行发包,公布的 yml 文件示例如下:
name: Publish package to NPM
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup pnpm env
uses: pnpm/action-setup@v2
with:
version: 6.32.16
- name: Setup node env
uses: actions/setup-node@v3
with:
node-version: 14
cache: 'pnpm'
- name: Install node_modules
run: pnpm install --frozen-lockfile
- name: Build module
# 进行包构建
run: pnpm build
- name: Run publish script
# 进行包公布
run: pnpm release
env:
NPM_TOKEN: ${{secrets.NPM_TOKEN}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
这样,当你的代码向 master 分支合入后,就会主动触发 npm 的公布行为,主动生成 tag、以及你 Github 我的项目的 Release。
值得注意的是,semantic-release 在初始化好之后就将你 package.json
中的版本号重置为 0.0.0-development
,并且在后续的迭代中,这个版本号都不会有任何扭转。这是因为 semantic-release 确定版本号的办法是从 tag 列表中获取最新的版本号,而并非是从 packageFiles
中获取版本号。这样的益处就是版本号不跟代码中的任何文件进行强关联,那么在 CI 迭代版本时也不会去批改源代码,造成 CI push 代码的行为。
3.2 手动配置
下面咱们探讨了应用 semantic-release-cli
疾速接入 Github 我的项目的计划,然而再理论的生产环境下是非常复杂的,兴许你应用的是 GitLab 亦或是其余平台,那么这时候往往须要咱们手动进行配置。
首先咱们须要在我的项目中装置 semantic-release
:
npm install semantic-release -D
之后咱们就能够应用 npx semantic-release
指令进行版本公布了,然而在此之前不得不提一下,semantic-release 大部分性能是由插件实现的,比方 npm 发包是由 @semantic-release/npm 插件实现的,在默认状况下 semantic-release 主动开启四个插件:
"@semantic-release/commit-analyzer"
"@semantic-release/release-notes-generator"
"@semantic-release/npm"
"@semantic-release/github"
当你应用 npx semantic-release
指令时 semantic-release 会通过多个阶段,在每个阶段的执行过程中 semantic-release 会做一些事件,比方生成 tag、推送 tag、编写 git notes 等,同时 semantic-release 的插件也会被触发,从而来『插手』做一些事件。在默认的状况下,npx semantic-release
会执行以下流程:
- 加载配置,确定哪些插件被启用;
- 加载插件;
- 执行
verifyConditions
阶段,这一阶段负责 校验用户以后的环境权限。比方 @semantic-release/npm 插件会查看是否有 .npmrc 配置以及是否有 npm token、@semantic-release/github 插件会查看以后环境是否有资格推送代码以及分支; - 执行
analyzeCommits
阶段,这一阶段负责 确定下一个版本是什么版本。比方 @semantic-release/commit-analyzer 在这一阶段会依据标准化的 commit 中来判断下一个版本是 major、minor 或 patch; - 执行
verifyRelease
阶段,这一阶段负责 验证行将公布的版本的参数(版本、类型、dist-tag 等); - 执行
generateNotes
阶段,这一阶段负责 生成公布阐明的内容; - 执行
prepare
阶段,这一阶段 负责筹备公布,例如创立或更新文件,比方package.json
中的版本号批改、tag 的生成就是在这一阶段产生的; - 执行
publish
阶段,这一阶段 负责执行公布相干的指令,@semantic-release/npm 就会在此公布 package,@semantic-release/github 会在此生成对应的 release; - 执行
addChannel
阶段,这一阶段 负责增加公布渠道,这里次要是让 @semantic-release/npm 调用 npm dist-tag 指令来为刚刚公布的包标记@latest
或@beta
等标签; - 执行
success
阶段,负责告诉新版本; - 执行
fail
阶段,负责告诉公布失败。
如果你想要增加更多的插件,能够参考这一章节。
3.3 注意事项
须要留神的是,semantic-release 实践上是能够反对所有的 CI 与 Git 托管平台的,然而须要留神一点就是你的 Git 托管平台必须能反对 CI 往上面有权限推送 tag 以及 git notes。
比方在应用 Gerrit Code Review 标准的平台上,推送代码仅反对 git push origin HEAD:refs/for/xxx
就造成无奈推送 refs/note/semantic
,那么 semantic-release 就会在 publish
阶段解体,导致前面的流程无奈持续。