在本地和CI/CD中支持npm免登录发布

1,746 阅读5分钟

专栏上篇文章传送门:函数库Rollup构建优化

本节涉及的内容源码可在vue-pro-components c8 分支找到,欢迎 star 支持!

前言

本文是 基于Vite+AntDesignVue打造业务组件库 专栏第 9 篇文章【在本地和CI/CD中支持npm免登录发布】,专门分享一下如何在 npm 发包时支持免登录发布,并同时支持在本地和CI/CD中操作发布流程。

组件库技术选型和开发环境搭建这篇文章中,我们简单介绍了怎么把一个包发布到 npm 上,但是执行lerna publish之前需要先验证登录,因为lerna publish它背后执行的还是npm publish,所以首先需要通过 npm 的认证流程。

image.png

一个流程中如果要执行登录流程,那么它的自动化程度就不会很高。如何解决这个问题呢?答案是 token,只要我们把 token 通过某个配置告诉 npm,就等同于告诉 npm 我是谁,所以只要这个 token 代表的是我的身份,自然就没必要输入账户密码登录了。

创建 token

我们先在 npm 网站 中登录,在用户下拉菜单这里能找到创建 token 的入口。

image.png

token 有两种,

image.png

一种是经典的通用 token,就是不限制使用范围,你名下的任何包/组织都能用这个 token 去管理。但是也大概分几种类型。如果要用在自动化流程中,需要避开双因素(2FA)验证,我们就创建 Automation 类型的 token。

image.png

还有一种就是更细粒度的 token,可以把权限控制到过期时间/IP范围/可读可写/包/组织等。

image.png

如果你觉得用界面操作很 Low,也可以选择极客风的命令行。npm 提供了创建 token 的命令行,具体见 npm token

怎么使用 token?

我们创建 token 主要是为了用于发布 npm 包。这个 token 我们可以配置在.npmrc文件中,对应的 key 是_authToken

image.png

//registry.npmjs.org/:always-auth=true
//registry.npmjs.org/:_authToken=your npm token

但是.npmrc文件一般是要提交到仓库中的,而 token 又是一个比较私密的数据,就不适合写死放在 .npmrc 中,此时我们可以使用变量替代,改成这样:

//registry.npmjs.org/:always-auth=true
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

那么这个变量可以从哪里读来呢?我们可以看看 npm 的一篇文档Set the token as an environment variable on the CI/CD server是怎么说的。

The npm cli will replace this value with the contents of the NPM_TOKEN environment variable.

答案是环境变量。这里要考虑 2 种情况,一个是本地化发布,一个是在 CI/CD 中发布。

首先说后面一种情况,在 CI/CD 中发布 npm 包已经有比较标准的方案了,大部分 CI/CD 平台都支持在 yaml 配置文件中指定环境变量,并且支持加密,没有暴露 token 的风险。上述文档中也有提到,关键配置如下:

steps:
  - run: |
      npm install
  - env:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

那么关键还是在于前面那种情况,有时候需要在本地发布 npm 包,此时应该怎么办呢?

我首先尝试添加系统环境变量,但是没有立即成功;

image.png

我还尝试了dotenv,虽然dotenv能加载.env文件到环境变量中,不过也不太方便。

如果.npmrc中存在变量NPM_TOKEN,跑任何npm scripts,都会去寻找${NPM_TOKEN},如果找不到就会报错,而我们不可能给所有脚本都加上dotenv

所以如果要在本地发布,一个替代方法是临时手动将.npmrc的 token 写死,改成:

//registry.npmjs.org/:_authToken=npm_xxxxxxxxxxxxxxxxxx

但是执行 lerna publish 的时候又需要一个干净的 git 状态,如果有 modified files 也不行(因为临时改了 .npmrc 就会导致 git 工作区不干净了)。啊,真难!最理想的办法还是把环境变量给搞定,同时又不能改太多脚本。

最后我发现加系统环境变量其实是有用的,关键是改了后要重新打开 VSCode(之前没有尝试这一步,导致我以为加系统环境变量没有用),否则终端加载不到最新的环境变量,果然还得是重启大法!所以最佳选择是使用变量${NPM_TOKEN}

本地验证 token 是否生效

搞定了环境变量后,我们先试试本地 publish 的场景。

考虑到之前用npm login或者npm adduser登录过,所以我们需要先退出登录再测试,否则无法确定是否 token 是否真的起了作用。

退出登录命令:

npm logout --registry=https://registry.npmjs.org

接着可以试试lerna publish或者npm publish,经测试已经不需要登录就能发布 npm 包了。

image.png

集成构建和发布流程

在集成构建和发布流程之前,我们参照@vue-pro-components/utils的构建流程把@vue-pro-components/headless的构建流程搞定,因为它们本质上都是函数库,打包过程不会有太多差异,抄一抄它不香吗?

image.png

同时根据各个包之间的依赖关系,新增一个统一构建的入口buildBatch,这样就能通过gulp buildBatch一条命令把所有的构建工作都做了。

继而可以得到一条集成构建和发布流程的命令release

"release": "yarn buildBatch && yarn publish:package",

所以只要我们把代码修改完毕,版本号确定之后,就可以执行yarn release进行发布了。

CI/CD workflow 搭建

Github 本身也支持 CI/CD,相关的产品是 Github Actions,所以我们可以直接使用它实现自动化构建和发布流程。

现在市面上有很多 CI/CD 工具,它们虽然在配置上有些差异,但是架构和理念都是相似的,学会使用一个,其他的参考着文档也基本能看得懂。

使用 Github Actions 主要就是写配置文件,我们可以基于官方的一些模板来初始化一个配置文件,这个 Publish Node.js Package 模板就比较合适。

image.png

你也可以通过阅读Github Actions 文档来了解更多相关知识。

我们按照模板文件改一改:

# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
name: Build and Publish Node.js Package

on:
  push:
    branches:
    - c*

env:
  NPM_TOKEN: ${{secrets.NPM_TOKEN}}

jobs:
  publish-npm:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 16
            
      - name: Install Dependency
        run: yarn install --frozen-lockfile
        
      - name: Build and Publish
        run: yarn release

因为这里要用到NPM_TOKEN变量,我们先到 Settings -> Secrets 中维护好变量。

image.png

修改一个版本号测试一下,一个简单版本的 CI/CD 这不就有了吗?

image.png

然后可以再加个 Cache 优化一下安装依赖的过程,这可以用到actions/cache@v3

结语

通过阅读和学习本文内容,我们已经能掌握怎么优雅地发布一个 npm 包,并同时支持了在本地和远程 CI/CD 中进行发布操作。但是我们应该注意到,每次发布都会执行完整的buildBatch过程,这个有没有必要呢?我想有时候是没有必要的,因为有可能某一个包根本就没修改过,但是每次发布时都执行打包过程就会浪费资源和时间。这里先留个疑问,后面文章接着讲。如果您对我的专栏感兴趣,欢迎您订阅关注本专栏,接下来可以一同探讨和交流组件库开发过程中遇到的问题。

技术交流&闲聊:前端司南