大家好,我是此林。
代码托管平台 Github 我们应该比较熟悉。每次我们提交代码到 GitHub 仓库时,特别是开源项目,一般都会自动触发测试脚本运行,帮你验证代码没有引入新的错误。
这个其实就是 GitHub Actions,一般我们把 YAML 脚本定义在 .github/workflows 目录下。
1. 什么是 GitHub Actions 自动 CI 测试?
GitHub Actions 是 GitHub 官方提供的一套自动化工作流服务,可以用来自动化代码构建、测试、部署等流程。CI(持续集成,Continuous Integration)测试 是指在代码每次提交或者合并请求时,自动运行项目的测试代码,保证代码的正确性和稳定性。
那为什么要用 GitHub Actions 自动 CI 测试?原因也很简单:
自动化:减少人工手动测试的工作,省时省力。
快速反馈:提交代码后马上知道测试结果,发现问题及时修复。
保障质量:避免有问题的代码被合并进主分支,提升代码质量。
多平台支持:可以在不同操作系统和环境下自动测试,比如 Windows、Linux、macOS。
免费且无缝集成:直接集成在 GitHub 仓库,无需额外配置第三方服务。
常见的 CI 测试比如:Node.js 项目跑 npm test、
Python 项目跑 pytest、
Java 项目跑 mvn test,
还有Docker 镜像构建和测试等等。
我们以 Seata 开源项目的 workflow s为例,作为讲解。
2. 整体结构
会看这个目录结构:
1. build.yml
Seata 是个 Java 项目,这是 CI 主流程,主要的话包括:
-
监听分支和PR:当代码被推送到
2.x
,develop
,master
分支,或者针对这几个分支发起 Pull Request 时,会自动触发这套构建和测试流程。 -
自动运行多版本 Java 测试:针对 Java 8、17、21 三个版本分别跑测试,保证代码兼容多个 JDK 版本。
-
做代码质量检查:在 Java 8 环境下执行 Checkstyle、PMD、License 检查。
-
上传代码覆盖率数据:通过 Codecov 上传测试覆盖率报告。
-
支持 ARM 架构构建:额外有一个任务在 ARM64 架构的 Ubuntu 容器里构建项目(跳过测试),用于生成 ARM 平台的二进制或包。
2. rerun-build.yml
这个配置是用来 自动重跑失败的 workflow 构建 的,只有当 build workflow 失败,并且重试次数还没达到 2 次时才执行。
3. publish-docker.yml
自动构建并发布 Docker 镜像到 DockerHub,针对三个不同版本的 Java(8、17、21)分别构建 Docker 镜像。
4. publish-ossrh.yml
将构建好的 Maven 包自动发布到 Maven Central(OSSRH 仓库)。
5. codeql-analysis.yml
CodeQL 是 GitHub 提供的代码静态分析工具,能够扫描代码中的安全漏洞、潜在缺陷和代码质量问题。它在每周六的 19:36(UTC 时间)定时触发一次全量扫描。
6. license-checker.yaml
简单点说,这个脚本配置是用来检查依赖 License 许可的,每个文件头部都必须加上这段注释。
5. test.yml、test-druid.yml、test-ubuntu.yml
这个就不多说,只有在真正 Seata 发版的时候才会触发,主要为多版本 Spring Boot 、多版本 Druid 测试专用,用于版本兼容性测试。
3. build.yml 详解
下面,我们来细看 build.yml。
3.1 触发条件
name: "build"
整个工作流的名称,在 GitHub Actions 的界面上显示。
on:push:branches: [ 2.x, develop, master ]
配置触发条件,当 push
到 2.x
、develop
或 master
分支时触发。
pull_request:branches: [ 2.x, develop, master ]types: [opened, reopened, synchronize]
当有人向这些分支提交了 PR 时也会触发 workflow,具体来说,比如这个 PR open了,或者close之后又 reopen了,synchronize 表示更新了 PR(如 rebase 后 push)。
paths-ignore:- '**.md'
如果改动只是 Markdown 文件(例如 README.md
)时 不触发 这个工作流。
3.2 Jobs 概览
真正 CI 的逻辑在两个 job 里,job1 是 Java 构建和测试(多个 JDK 版本),job2 是 ARM64 编译构建。我们重点看 job1。
services 里是启动 Redis 和 Nacos 服务容器 ,测试会用到。
然后操作系统为 Ubuntu 最新版。
strategy 里的含义:
-
使用构建矩阵:分别用 Java 8、17、21 构建。
-
fail-fast: false
表示一个失败不会立即取消其他并行任务。
3.3. Steps 详解
总共有八个 steps,我们一个一个看。
1. 拉取代码
- name: "Checkout"uses: actions/checkout@v3
2. 设置 Python 3.12(某些依赖或构建脚本可能用到)
- name: "Use Python 3.x"uses: actions/setup-python@v2with:python-version: '3.12'
3. 设置 Java,版本用的就是之前 strategy 里定义的 matrx.java。
- name: "Set up Java JDK"uses: actions/setup-java@v3.12.0with:distribution: 'zulu'java-version: ${{ matrix.java }}
4. 打印 Maven 版本
- name: "Print maven version"run: ./mvnw -version
5. Maven 缓存,提高构建速度,缓存 .m2
目录。
- name: "Restore local maven repository cache"uses: actions/cache/restore@v4id: cache-maven-repositorywith:path: ~/.m2/repositorykey: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }}restore-keys: |${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}${{ runner.os }}-maven-
6.1 Java 8:完整测试 + PMD + License 代码检查
- name: "Test, Check style, Check PMD, Check license with Maven and Java8"if: matrix.java == '8'run: |./mvnw -T 4C clean test \-Dcheckstyle.skip=false -Dpmd.skip=false -Dlicense.skip=false -DredisCaseEnabled=true \-e -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
6.2 Java 17 和 21:仅测试
- name: "Test with Maven and Java${{ matrix.java }}"if: matrix.java != '8'run: |./mvnw -T 4C clean test \-e -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
7. 保存 Maven 缓存(如果前面没命中缓存)
- name: "Save local maven repository cache"uses: actions/cache/save@v4if: steps.cache-maven-repository.outputs.cache-hit != 'true'with:path: ~/.m2/repositorykey: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }}
8. 上传覆盖率(只在 Java 8)
- name: "Codecov"if: matrix.java == '8'uses: codecov/codecov-action@v4.0.1with:token: ${{ secrets.CODECOV_TOKEN }}version: v0.6.0env:CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
4. rerun-build.yml 详解
这个在 build
的 workflow 失败时,自动尝试重新运行一次,最多尝试 1 次。
on:workflow_run:workflows: ["build"]types:- completed
触发条件:当 build
这个 workflow 执行“完成”(completed
)后触发,不管成功与否。
问:之前不是说失败的时候才触发吗?
答:别急,后面 jobs 里会有 if 条件判断。
Job 条件:
只有当
build
workflow 执行失败(failure) 时才会触发这个 job。
run_attempt
是执行次数,如果是第一次失败就触发(< 2,表示最多自动 retry 一次,防止死循环)。
操作系统还是 ubuntu-latest。
- name: rerun ${{ github.event.workflow_run.id }}
Step 名称:会显示在 Actions 的执行日志中。
env:GH_REPO: ${{ github.repository }}GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
设置环境变量:
-
GH_REPO
:当前仓库名 -
GH_TOKEN
:自动注入的 GitHub Token,用于调用 GitHub CLI
run: |gh run watch ${{ github.event.workflow_run.id }} > /dev/null 2>&1gh run rerun ${{ github.event.workflow_run.id }} --failed
执行脚本:
-
gh run watch:
等待build
workflow 的状态稳定 -
gh run rerun:
只重跑失败的 job(节省资源)
今天的分享就到这里了。
我是此林,关注我吧,带你看不一样的世界!