关于npm:npm-自动化发包方案与实现

5次阅读

共计 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 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 指令时,以后的工作区必须是洁净的 ,否则会执行失败;且当执行胜利后,会主动生成一个 commit(commit message 默认为版本号), 同时在这次主动生成的 commit 上打一个 tag,tag 名称即为以 v 结尾的版本号名称,如果你想批改默认的 commit message,你能够应用如下指令:

npm version patch -m "Release version %s" #『%s』代表以后版本号

此外,对于你公布的 prerelease 版本的 package 须要留神以下两点:

  1. 当用户进行首次装置你的包时,且此时你的包最新的版本为一个 prerelease 版本,那么用户就会装置这个 prerelease 版本;如果用户只想装置稳定版,那么能够通过 npm install xxx@version,比方 npm install xxx@1npm install xxx@">1.0.0" 这样的指令装置的包不会装置到 prerelease 版本。
  2. 然而,当用户以后装置的是一个正式版本的包时,应用 npm update 去更新你的包,是不会被动更新到 prerelease 版本的;但如果正式版用户想要降级为 prerelease 版,能够通过执行 npm install package@latest 来装置最新的版本(蕴含预览版)。

1.3 CHANGELOG 的生成

在一些我的项目中,会用 CHANGELOG.md 来标注每个版本的变更内容,这个文件通常是应用专门的工具生成的,比方 conventional-changelog,然而主动生成的条件必须满足:

  1. 应用规范的 commit 标准,通常在默认状况下应用 Angular 的提交标准,这样 conventional-changelog 就会晓得你每次提交做了什么,是新增了一个 fetature,还是修复了一些 bug,亦或是其余。你能够应用 @commitlint/cli + husky 对你的代码进行提交查看,同时也能够应用 commitizen 来生成标准化的 commit,对于这些,你能够参考这篇文章。
  2. 在每次生成一个新的版本后,在以后的提交上要创立一个 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。

总之,将所有的流程配置好之后,残缺的工作流如下:

  1. 编辑代码,增加新性能或者修复 bug;
  2. 实现某个性能后进行 commit,commit 要合乎 Angular 的提交标准;
  3. 持续实现其余的性能,并每实现一个性能后及时提交标准化的 commit,直到你想要发版为止;
  4. 执行 npm version xxx 生成新的版本号,这时 CHANGELOG 和版本号都会主动进行迭代;
  5. 执行 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 会做如下的事件:

  1. 读取 package.json 查问以后包的版本号,如果没有查问到,就将最初一个 tag 的版本号视作以后的版本号;
  2. 根据你的 commit 信息,来决定下一个版本号(这一过程被称为 bump version),而后批改 package.jsonpackage-lock.json 等须要迭代版本号的文件中的版本号字段;
  3. 根据你的 commit 信息生成或更新 CHANGELOG.md 文件;
  4. 应用新的版本号为名称,创立一个 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 会执行以下流程:

  1. 加载配置,确定哪些插件被启用;
  2. 加载插件;
  3. 执行 verifyConditions 阶段,这一阶段负责 校验用户以后的环境权限。比方 @semantic-release/npm 插件会查看是否有 .npmrc 配置以及是否有 npm token、@semantic-release/github 插件会查看以后环境是否有资格推送代码以及分支;
  4. 执行 analyzeCommits 阶段,这一阶段负责 确定下一个版本是什么版本。比方 @semantic-release/commit-analyzer 在这一阶段会依据标准化的 commit 中来判断下一个版本是 major、minor 或 patch;
  5. 执行 verifyRelease 阶段,这一阶段负责 验证行将公布的版本的参数(版本、类型、dist-tag 等);
  6. 执行 generateNotes 阶段,这一阶段负责 生成公布阐明的内容
  7. 执行 prepare 阶段,这一阶段 负责筹备公布,例如创立或更新文件,比方 package.json 中的版本号批改、tag 的生成就是在这一阶段产生的;
  8. 执行 publish 阶段,这一阶段 负责执行公布相干的指令,@semantic-release/npm 就会在此公布 package,@semantic-release/github 会在此生成对应的 release;
  9. 执行 addChannel 阶段,这一阶段 负责增加公布渠道,这里次要是让 @semantic-release/npm 调用 npm dist-tag 指令来为刚刚公布的包标记 @latest@beta 等标签;
  10. 执行 success 阶段,负责告诉新版本;
  11. 执行 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 阶段解体,导致前面的流程无奈持续。

正文完
 0