likes
comments
collection
share

使用GitHub Actions实现自动化部署

作者站长头像
站长
· 阅读数 13

我实现的一个小工具genCode4Swagger,这个工具的作用是使用swagger文档在项目中生成接口方法,通过构建Github Actions工作流,我实现了:

  1. 推送分支到远程主分支时,自动生成README.md文档,如果README.md修改了,将修改push到主分支中。
  2. 在版本修改时,自动执行构建,并且通过npm publish发布为npm

关于GitHub Actions

GitHub Actions 是 GitHub 提供的一项持续集成(CI)和持续部署(CD)服务,它使您可以在 GitHub 仓库中自动化执行各种工作流程。使用 GitHub Actions,您可以创建自动化流程,以响应存储库事件,例如代码推送、拉取请求、标签创建等。这些工作流程可以帮助您构建、测试、部署和其他与项目相关的任务。

在GitHub Actions中,每一个运行的步骤的步骤都由workflow (工作流程)来定义,存放在代码仓库的.github/workflows目录下。

workflow文件采用YAML格式,一个仓库下可以定义多个workflow文件。GitHub会自动发现.github/workflows目录下的*.yml文件,然后自动运行该文件。

workflow通过on字段设置触发条件,如在push到主分支时触发:

on:
  push:
    branches:
      - main

workflow中可以定义多个 job (任务),换而言之,workflow其实就是多个job的集合,而job是由多个step(步骤)构成,一步步执行;然后每个step又能执行多个action (动作),比如定义一个job检出代码:

jobs:
  checkout:
    runs-on: ubuntu-latest

    steps:
    - name: 检出代码
      uses: actions/checkout@v2

jobs字段下的每一项都表示一项任务,然后runs-on定义了运行环境,steps表示开始定义步骤,然后使用name标注step的名称,uses是使用已经定义好的action,step还有配置,常用的有:

  • run:执行脚本命令;
  • with:向步骤提供参数配置,将会被传递给使用的action,和uses一起使用;
  • env:用于设置环境变量,这些环境变量将会在run执行的脚本命令中使用;

使用name字段,为workflow定义一个名字,组合上面的代码,就可以得到一个在push到主分支时,自动检出代码的工作流:

name: 我的第一个workflow

on:
  push:
    branches:
      - main

jobs:
  checkout:
    runs-on: ubuntu-latest

    steps:
    - name: 检出代码
      uses: actions/checkout@v2

这个工作流没有实际的意义,只是做一个示例来说明workflow的基本配置,有了一定了解后,再让我们来实现一下实际项目中的工作流吧!

生成文档

我的基本诉求是生成文档;生成文档的时机是在项目推送到远程分支上时,附加的需求是希望在生成文档和原本的文档有差别时,将更新通过git推送到远程分支上。

首先,我们创建文件.github/workflows/documentation.yml,将name定义为文档构建,然后定义工作流触发时机:

name: 文档构建

on:
  push:
    branches:
      - main

使用jsdoc生成文档

生成文档我使用的是jsdoc-to-markdown,这个工具可以使用文件中的jsdoc注释来生成文档,配置为npm script:

"build:doc": "jsdoc2md --files ./src/index.ts --configure ./jsdoc2md.json > README.md"

./src/index.ts的jsdoc注释中,生成文档到README.md文件中。

在工作流中,生成文档直接使用pnpm run build:doc命令即可,执行命令之前,我们需要一些步骤来构建执行命令的环境:

  1. 检出代码
- name: 检出代码
	uses: actions/checkout@v2
  1. 设置Node.js并指定版本
- name: 设置 Node.js
  uses: actions/setup-node@v2
  with:
	node-version: 'v16.18.0'
  1. 因为项目中使用的是pnpm所以需要安装
- name: 安装 pnpm
  run: npm install -g pnpm
  1. 然后安装项目依赖
- name: 安装依赖
  run: pnpm install
  1. 最后,再运行pnpm run build:doc命令构建文档
- name: 构建文档
  run: pnpm run build:doc

检测新文档并且push

再来分析一下我们的附加需求,生成新文档时,git push到远程分支,推送到远程分支简单,git push命令就可以了,我们先确定最后的步骤:

- name: 提交并推送更改
  run: git push

然后我们再考虑如何检测新文档,使用git命令想要检测文件是否修改其实很简单,使用git diff就可以, git diff 可以显示两个 Git 树之间的更改,如果没有指定树(例如提交或分支),则默认比较工作目录和暂存区。如果发现文件改动就可以执行git add和git commit提交代码了,完整的脚本如下:

git diff --quiet --exit-code README.md || (git add README.md && git commit -m 'Update README.md')

选项--quiet 使 git diff 在发现有更改时立即退出,而不显示这些更改,选项 --exit-code 使 git diff 在发现有更改时返回非零退出码,git diff --quiet --exit-code README.md就是在发现有更改时返回非零退出码,|| 是一个 shell 操作符,用于在前一个命令失败时(即返回非零退出码)执行后一个命令。

将脚本配置为npm script:

"postbuild:doc": "git diff --quiet --exit-code README.md || (git add README.md && git commit -m 'Update README.md')",

然后在git push的步骤之前添加:

- name: 提交修改
  run: pnpm postbuild:doc

这里有一点需要注意,如果我们的包管理工具使用的是npm,那其实是不需要这一步的,因为prepost都是特殊的前置,prebuild会在npm build执行前触发,postbuild会在npm build执行后触发,所以使用npm run build:doc之后会自动执行npm run postbuild:docpnpm并没有这样的规则,所以需要手动触发。

在workflow中,我们不需要安装git,但我们需要设置git账号信息:

- name: 设置git信息
  run: |
	git config --local user.email "action@github.com"
	git config --local user.name "GitHub Action"

现在可以把我们的工作流推送到远端,就能够自动执行,如果文档有更新,会自动生成,然后更新README.md文件。

GitHub Token

在上文我们将工作流推送到远端后,工作流并没有成功执行,而是在执行git push时报错:

Permission denied to github-actions[bot]

这是因为我们当前的工作流没有权限去推送代码,我们需要先去项目中进行设置,在项目地址中的Settings->Actions->General下的Workflow permissions菜单下,我们可以看到两个选项:

使用GitHub Actions实现自动化部署 项目默认选中的是第二个选项,此时工作流程在存储库中仅具有内容和包范围的读取权限,我们需要选中第一个选项,工作流才能中具有读取和写入权限。

再尝试执行工作流就能够正常运行了。

自动发布

第二个需求,是在版本更新时,自动发布为npm包;版本更新时,我们需要修改package.json的version,这是npm发版的需求,在我们的项目管理中,我们需要标记不同版本的代码,方法在发生问题时,进行回滚和检测,比较常见的方式就是实用git标记tag,我们使用standard-version来实现这些功能。

standard-version实现版本更新

standard-version 是一个用于版本管理和发布的工具,它遵循语义化版本(Semantic Versioning)规范,并自动生成 CHANGELOG。在package.json中添加release脚本:

"scripts": { "update:version": "standard-version" }

然后使用npm run update:version运行脚本,standard-version会执行一下操作:

  • 更新版本号:standard-version 会根据你的 git 提交历史自动更新 package.json 中的版本号。
  • 生成或更新 CHANGELOG:standard-version 会根据你的 git 提交历史自动生成或更新 CHANGELOG.md 文件。
  • 创建一个新的 git tag:standard-version 会创建一个与新版本号对应的 git tag。
  • 提交更改和 tag:standard-version 会将更改提交到 git。

standard-version不会提交tag到远端,我们需要添加一段git脚本,来实现完整的发布功能:

"scripts": {
	"release": "npm run update:version && git push --follow-tags origin main"
}

需要发布版本时,运行pnpm run release命令,然后剩下的交给工作流即可。

构建发布工作流

明确工作流触发时间,在推送tag时触发:

on:
  push:
    tags:
      - v*  # 触发工作流的 tag 模式,v* 表示所有以 v 开头的 tag

npm publish发布的准备工作:

  1. 检出代码
  2. 设置Node.js版本及需要发布npm的registry
  3. 安装pnpm
  4. 执行构建
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: 检出代码
      uses: actions/checkout@v2

    - name: 设置 Node.js
      uses: actions/setup-node@v2
      with:
        node-version: 'v16.18.0'  # 使用你喜欢的 Node.js 版本
        registry-url: 'https://registry.npmjs.org'

    - name: 安装 pnpm
      run: npm install -g pnpm

    - name: 安装依赖
      run: pnpm install  # 或者根据你的包管理器使用 'npm ci' 或 'yarn'

    - name: 构建
      run: pnpm run build  # 替换为你用来构建项目的命令

最后就是发布任务,但是发布到npm仓库是需要鉴权的,所以我们需要使用env设置NODE_AUTH_TOKEN

- name: 发布 npm 
  run: npm publish
  env:
	NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

在你登录过npm账号的电脑上(没有登录过使用npm login登录你的账号),执行cat ~/.npmrc,然后_authToken之后的值就是我们需要的Token;在你的GitHub项目中,Settings-> Secrets and variables-> Actions,Secrets选项卡下,点击New repository secret按钮,添加NPM_TOKEN。

使用GitHub Actions实现自动化部署

最后本地执行pnpm run release,项目推送到远端,工作流自动运行,npm包就能够自动发布了。

总结

在本文中,我们配合git命令,使用jsdoc-to-markdown将项目中的jsdoc注释生成文档,使用standard-version进行版本更新 还是自动发布,使用Github Action工作流编排任务,然后在合适的时机触发来实现了项目的自动化部署。

Github Action本质上是一个编排工具,实现自动化部署我们也可以选择其他工具来实现,使用这类工具时,我们可以将需求告诉chatgpt,借助AI来快速编写workflow。

在生成文档的工作流中,最后git push到远端,理论上会再次出发工作流,在一些情况下可能进入死循环,所以如果需要再工作流中推送代码要额外注意,生成文档这个需求更好的方式应该是在本地git push之前,自动生成,跟随本地的修改一起提交,但是在本文就不再讨论了。

最后是国内github访问不稳定的问题,我已经配置好了本地host,但是依旧不稳定,然后通过 git 配置代理来实现的(前提是你有稳定的代理):

git config --global http.https://github.com.proxy socks5://127.0.0.1:1080