上一篇我们讲了如何使用 ESLint、Prettier、Husky 等工具约束代码开发、提交规范。
本文和大家分享如何利用 Github Actions 部署一套 CI/CD,推送代码后自动发布 NPM。
成品可以参考 utils ,欢迎 star 🤞❤️
在部署 CI/CD 之前,我们还有一个小尾巴,就是完善 README ,一份好的 REAMME 不光能让人看懂内容,更要让人油然而生一种赏心悦目的美感。知名的开源项目,README 通常写得都非常好,而且还有很多的 badge,比如 Ant Design。
为 README 添加 badge (徽章)
之前没怎么注意过这个东西,点开 README 看了看,发现是一些图片链接。这些链接都是由 shields 网站一键生成的,里面琳琅满目各种徽章应有尽有。具体的格式可以参考 《如何在github的README中增加Badge标识》,写得很详细。这里,我们以许可证 License 为例,示范一下如何使用:
- 侧边栏找到需要的徽章;
- 右边输入仓库信息(用户名、组织名、仓库名等信息);
- Execute 一键生成。
值得一提的是,测试覆盖率(coverage)需要一些前置准备,稍微有些麻烦:
- 进入 coveralls 官网,进行 Github 授权;
- 授权后点击左侧侧边栏的 ADD REPOS ,将需要生成 badge 徽章的库设置为 on;
- 在 CI 中执行测试脚本并生成测试覆盖率的文件然后上传到 coveralls 就可以了。
Github Actions
GitHub Actions 是一种持续集成和持续交付 (CI/CD) 平台,可用于自动执行生成、测试和部署管道。
生成 NPM_TOKEN
发布 NPM 包之前,我们需要做一些前置工作,在 NPM 中生成 NPM_TOKEN
,用它打通 GitHub 和 NPM。
首先,进入 NPM 首页,登录后点击头像,选择 Access Tokens
,点击 Generate New Token
,选择 Classic Token
就可以了。
接着,给 token 起个名字,并选择 publish
权限。
生成 token 后记得保存,关闭页面后就再也看不到了。
生成 Github Repositor secrets
回到 Github 仓库中来,首先点击 Settings
,然后选择 Secrets and variables
下的 Actions
,再点击 New repository secret
,取名 NPM_ACCESS_TOKEN
,并将先前生成的 NPM_TOKEN
填入,最后 add secret
。
请注意这里取的名字,要与 Workflows 脚本中的一致,后面就可以在 CI 中通过 secrets.NPM_ACCESS_TOKEN
获取到。
构建 Workflows 流水线
Workflows 是一个可配置的自动化流程,一条 Workflows 可运行一个或多个 Job 。
Workflows 由签入到存储库的 YAML 文件定义,并在存储库中的事件触发时运行,也可以手动触发,或按定义的时间表触发。
Workflows 定义在存储库的
.github/workflows
目录中,存储库可以有多个 Workflows,每个 Workflows 都可以执行不同的任务集。
1. 创建脚本
点击 Tab 栏上的 Actions
,我们要发布到 NPM,所以选择其中的 Punlish Node.js Package
。
此时,会自动新建一个 npm-publish.yml
脚本,修改脚本后,点击右上角 Commit changes
保存并提交,在项目中就会生成对应的脚本文件。
当然,你也可以直接在本地新建 .github/workflows/npm-publish.yml
文件,修改完后再上传。
2. 创建 build job
这里,有一个名叫 Node.js CI
的 Workflows,我们给它添加第一个任务,取名 build
,用来打包构建项目。具体内容如下:
yml
jobs:
# 创建了一个名叫 'build' 的 job
build:
# 指定了操作系统为 'ubuntu-latest'
runs-on: ubuntu-latest
# 设置变量
strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
# steps 代表一系列任务作为作业的一部分而被执行
steps:
# 检查仓库以便作业能正常访问
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm install
# 测试,并生成测试覆盖率文件
- run: npm run coveralls
# 打包
- run: npm run build
# 查看打包后的目录文件
- run: ls -a
# 上报
- name: 上报 Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
3. 创建 publish-npm job
接着,我们创建第二个任务,取名 publish-npm
,用它来进行 NPM 发包。具体内容如下:
yml
# 创建 publish-npm 任务
publish-npm:
# 在 ubuntu 最新版本的虚拟机执行
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.x ]
steps:
# 检查并切换到 main 分支
- name: 检查 main 分支
# 使用 actions/checkout 插件
uses: actions/checkout@v3
# 初始化缓存
- name: 初始化缓存
uses: actions/cache@v3
id: cache-dependencies
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}
# 安装 node
- name: 安装 Node.js
# 使用 actions/setup-node 插件
uses: actions/setup-node@v3
with:
# node版本
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build
# 读取当前版本号
- name: 读取当前版本号
id: version
uses: notiz-dev/github-action-json-property@release
with:
# 读取版本号
path: './package.json'
prop_path: 'version'
- run: echo ${{steps.version.outputs.prop}}
- name: 创建 Release
uses: softprops/action-gh-release@v1
with:
files: ./lib/index.umd.js
name: v${{steps.version.outputs.prop}}
tag_name: v${{steps.version.outputs.prop}}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 发布 NPM 包
# 执行发布代码
run: |
npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN
npm publish
env:
# 配置 npm access token 环境变量
NPM_TOKEN: ${{secrets.NPM_ACCESS_TOKEN}}
- name: 刷新缓存
run: |
curl https://purge.jsdelivr.net/npm/iemotion-pic@latest/lib/name.json
现在,我们的 Workflows 结构应该长这样:
当 main
分支有新的推送时,就会触发 Actions 自动执行 Workflows 中的两个 Job。
FAQ
1. GitHub release failed with status: 403
md
Run softprops/action-gh-release@v1
👩🏭 Creating new GitHub release for tag v1.0.0...
⚠️ GitHub release failed with status: 403
undefined
retrying... (2 retries remaining)
👩🏭 Creating new GitHub release for tag v1.1.0...
⚠️ GitHub release failed with status: 403
undefined
retrying... (1 retries remaining)
👩🏭 Creating new GitHub release for tag v1.0.0...
⚠️ GitHub release failed with status: 403
undefined
retrying... (0 retries remaining)
❌ Too many retries. Aborting...
Error: Too many retries.
这是因为当前的 GITHUB_TOKEN
没有该仓库创建 release 的权限,在 Wrokflows 中添加 contents
属性,并赋予其 write
操作权限:
yml
permissions:
contents: write
GITHUB_TOKEN
还有其他权限,如果想修改默认权限,可以查阅 👉 Assigning permissions to jobs
2. 402 Payment Required - You must sign up for private packages
md
npm ERR! code E402
npm ERR! 402 Payment Required - PUT https://registry.npmjs.org/@zerozhang%2futils - You must sign up for private packages
...
Error: Process completed with exit code 1.
这是因为当前包的私有属性配置不当所致。对于一般的包,我们在 package.json
中添加 private: false
即可,但是如果是范围包(即包的名称以 @org-name/ 开头) ,仅仅配置 private
还不够,还需要搭配 publishConfig
来使用:
json
{
"private": false,
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
3. You cannot publish over the previously published versions: 1.0.0.
md
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT <https://registry.npmjs.org/@zerozhang%2futils> - You cannot publish over the previously published versions: 1.0.0.
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.
Error: Process completed with exit code 1.
这是因为当前包的版本没有更新所致。如果要进行迭代,那么必须提升包的版本号,不允许在原来的版本号上继续迭代。
所以,main
分支在合并完代码后,不要急着推送,而是先使用 npm version
进行升级。此时,package.json
和 package-lock.json
中的 version
会进行相应的提升。再进行迭代发布就没有问题了。
- npm version patch
1.0.0--> 1.0.1
- npm version minor
1.0.0--> 1.1.0
- npm version major
1.0.0--> 2.0.0
顺便提一嘴,如果是迭代发布,请做好更新日志的说明,在项目根目录中创建 CHANGELOGO.md
文档,记录好本次迭代的更新内容,这是一个规范开源项目的基本操作。
总结
- 如何使用 shields 给 README 添加 badge 徽章;
- 如果使用 Github Actions 构建 Workflows 自动执行 NPM 发布;
- Bugfix 处理与答疑。
往期文章
《从零开发自己的工具库(一)配置 TS + Rollup + Jest》
《从零开发自己的工具库(二)配置 ESLint + Perttier + Husky + Commitlint》
参考资料
使用Typescript和Rollup从零开发一个工具库, 并使用Github Actions进行CI操作