GitHub Actions 基础:workflow、job 和 step
1. 概述
GitHub Actions 是 GitHub 内置的 CI/CD 工具,帮助你自动化构建、测试、部署,无需单独的 CI 服务器。本文解释三个核心概念:workflow、job 和 step,并附带一个使用 Maven 的 Spring Boot 项目实例。
读完本文后,你将能够编写一个在每次推送代码时自动运行测试的 workflow 文件,理解 job 并行或顺序运行的方式,以及当 workflow 失败时如何调试。
2. Workflow 是什么?
Workflow 是一个 YAML 文件,位于仓库的 .github/workflows/ 目录中。每个 workflow 定义何时运行(trigger)以及做什么(jobs)。
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build with Maven
run: mvn -B package
解释:
- 文件开头的
name是在 GitHub 的 Actions 标签页上显示的名称。 on声明触发器:这里 workflow 在向main分支进行push或pull_request时运行。jobs.build是一个名为build的单一 job,运行在ubuntu-latest虚拟机上。
注意: 文件必须位于正确的路径
.github/workflows/*.yml,如果目录错误,GitHub 将无法识别 workflow。
3. Job:独立的执行单元
一个 workflow 可以包含多个 job。每个 job 运行在一个独立的 runner(虚拟机)上。默认情况下,job 是并行运行的,除非你声明 needs。
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- run: echo "Deploying application..."
解释:
test和deploy是两个独立的 job,每个 job 拥有干净的 runner 环境,彼此不共享文件系统。needs: test强制deploy必须等待test成功后才能运行——创建了顺序关系。- 如果没有
needs,GitHub Actions 会同时运行test和deploy。
3.1. Job 间共享数据
由于每个 job 有独立的 runner,如果要在 job 之间传输文件,需要使用 artifacts。
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B package
- uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
with:
name: app-jar
解释:
upload-artifact将buildjob 中的.jar文件保存到 GitHub 临时存储中。deployjob 中的download-artifact从新的 runner 上重新下载该文件。- 这是在 job 之间传输数据的唯一方式(不能使用普通环境变量)。
4. Step:最小的操作单位
Step 是 job 中的具体操作,从上到下依次执行。Step 主要有两种类型:使用 uses 调用预设的 action,或使用 run 执行 shell 命令。
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- name: Run tests
run: mvn -B test
- name: Print result
run: echo "Test step finished with status ${{ job.status }}"
解释:
Checkout codestep 使用预设的actions/checkoutaction 将源代码拉取到 runner。Cache Maven dependenciesstep 缓存~/.m2/repository目录,使后续构建更快,因为不需要重新下载依赖。- 缓存的
key基于pom.xml的哈希值——当pom.xml变化时,旧缓存被丢弃,创建新缓存。 - 最后一个 step 使用
run执行普通 shell 命令,可以引用上下文变量如${{ job.status }}。
5. 环境变量和 secrets
你经常需要向 workflow 传入敏感配置(API 密钥、token),但不能在 YAML 文件中硬编码。
jobs:
deploy:
runs-on: ubuntu-latest
env:
SPRING_PROFILES_ACTIVE: prod
steps:
- uses: actions/checkout@v4
- name: Deploy to server
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying with profile $SPRING_PROFILES_ACTIVE"
curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy
解释:
- job 级别的
env对该 job 中的所有 step 有效(SPRING_PROFILES_ACTIVE)。 - step 级别的
env仅对该 step 有效(DEPLOY_TOKEN)。 secrets.DEPLOY_TOKEN从仓库的 Settings → Secrets and variables → Actions 获取,实际值永远不会在日志中显示。
注意: 永远不要直接在日志中打印 secret 值,比如用
echo $DEPLOY_TOKEN——GitHub 会自动屏蔽,但最好还是避免打印 secret。
6. Matrix:同时运行多个版本
需要在多个 Java 版本或操作系统上测试时,matrix 可以避免重复编写 job。
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [17, 21]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- run: mvn -B test
解释:
strategy.matrix.java声明了值列表[17, 21]。- GitHub Actions 自动创建 2 个并行 job,每个 job 使用不同的 Java 版本。
- 这种方法节省了编写重复 YAML 的工作,适合需要在多个环境上验证代码的情况。
7. 通过简单测试验证 workflow
Workflow 不能直接用 JUnit 测试,但你应该确保 workflow 中的 mvn test 步骤真正运行项目的测试套件。以下是一个简单的 OrderService 测试示例,CI 会自动运行:
class OrderServiceTest {
private final OrderService orderService = new OrderService();
@Test
void shouldCalculateTotalPriceCorrectly() {
Order order = new Order("ORD-001", List.of(
new OrderItem("Keyboard", 2, BigDecimal.valueOf(50)),
new OrderItem("Mouse", 1, BigDecimal.valueOf(20))
));
BigDecimal total = orderService.calculateTotal(order);
assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(120));
}
}
解释:
- 该测试位于
src/test/java,会被 workflow 步骤中的mvn -B test自动运行。 - 如果测试失败,
Run tests步骤会报错(退出码非 0),job 停止,依赖的 job(通过needs)不会继续执行。 - 这就是 CI 有用的原因:有问题的代码在合并到
main之前就被阻止了。
在 GitHub Actions UI(Actions 标签页)上的运行结果:
✓ Checkout code
✓ Set up JDK 21
✓ Build with Maven
✓ Run tests (shouldCalculateTotalPriceCorrectly PASSED)
8. 常见错误
- YAML 缩进错误:GitHub Actions 使用 YAML,缩进错误一个空格会导致 job 或 step 无法识别,通常报错“Invalid workflow file”。
- 遗漏
runs-on:每个 job 必须有runs-on,缺少它会导致 workflow 在解析时立即失败。 - 在另一个 job 中使用本 job 的环境变量:普通的
env和变量输出不会自动跨 job 传递,必须使用outputs或artifact。 - 依赖变化时缓存 key 不更新:如果
key设置过于通用(比如只用maven-cache),会错误地使用包含过时依赖的旧缓存。 - 在每次推送时运行繁重的 workflow:如果不限制
paths或branches,每个小的提交(比如修改 README)都会触发完整的构建+测试 pipeline,浪费 Actions 的免费分钟数。
注意: 如果项目只需要简单的构建且没有多个环境,就不需要使用
matrix或artifact——不必要的复杂性会让 workflow 难以维护。
9. 总结
Workflow 是定义整个 pipeline 的 YAML 文件,job 是在独立 runner 上运行的独立执行块,step 是 job 内的具体操作。理解这三个层次可以帮助你清晰地组织 CI/CD pipeline:哪些 job 并行运行,哪些通过 needs 相互等待,以及何时使用 artifact 共享数据。
刚开始时,保持 workflow 简单(一个 job、几个基础 step:checkout、setup Java、test)。随着项目增长,添加 matrix、cache,并将 build/test/deploy 拆分成独立的 job 以便于管理和调试。