前置知识: GitHub

GitHub Actions 与 CI/CD

9 min中级

GitHub Actions 与 CI/CD 总纲:以智能工厂流水线为类比,讲透 CI/CD 概念、workflow 文件结构(name/on/jobs/steps)、Actions 市场使用与工程最佳实践。

前置知识

建议先阅读以下内容再进入本文:

0. 开始之前:一座”智能工厂流水线”的故事

想象一座现代化工厂:原材料进厂(代码提交),传送带把零件送到各个工位——质检工位自动检查(lint)、测试工位自动试运行(test)、组装工位打包成品(build)、发货工位把货送到客户(deploy)。整条流水线由一套中央控制系统自动调度:原料一到,各工位按顺序自动开工;质量不合格,立刻亮红灯拦截;货品信息全部记录在案。

GitHub Actions 就是 GitHub 内置的这套”智能工厂流水线”——一套 CI/CD(持续集成 / 持续交付) 自动化平台。你只需要用 YAML 描述”工位清单”(workflow 工作流),GitHub 就会在云端”传送带”(runner 运行器)上自动完成:构建、测试、打包、部署,还能对仓库里的其他事件(开 Issue、发 Release)自动响应。

本文是 Actions 系列的总纲:先把 CI/CD 概念讲明白,再拆解 workflow 文件结构,最后给出 Actions 市场使用指南与最佳实践。后续各篇(触发器、矩阵、缓存、制品、环境)都是本篇某个环节的深入。

1. CI/CD 是什么:为什么每个仓库都需要

1.1 CI(持续集成,Continuous Integration)

核心思想:频繁地把代码合并到主干,并在每次合并前自动构建和测试,尽早发现集成问题。

  • 开发者在 PR 里提交代码 → 自动跑一遍测试 → 通过才能合并。
  • 好处:问题在几小时内暴露,而不是发布前一天才发现。

1.2 CD(持续交付/持续部署,Continuous Delivery/Deployment)

持续交付:代码合并后自动准备好”随时可发布”的产物(构建 + 测试 + 打包)。 持续部署:在持续交付基础上,把发布这一步也自动化——合并到 main 自动上生产。

CI:   代码提交 → 自动构建 → 自动测试 → 汇报结果
CD:   CI 通过 → 自动打包 → 部署 staging → (审批)→ 部署生产

1.3 为什么用 GitHub Actions

优势说明
零配置接入与 GitHub 仓库天然集成,不用单独搭服务器
生态丰富GitHub Marketplace 有大量现成 Action 可复用
免费用量公开仓库免费,私有仓库有免费分钟额度
事件驱动push、PR、Release、定时、外部 API 都能触发
可观测Actions 页面可视化查看每次运行日志与状态

2. 核心组件总览:认识流水线的”零件”

GitHub 官方把 Actions 的组件划分为六个概念,层级从小到大依次是:

workflow(工作流)→ jobs(任务)→ steps(步骤)→ actions(动作)/ shell 命令
                                        ↕
                    runner(运行器:执行这些任务的机器)
                    event(事件:触发流水线开动的信号)
组件中文说明
Workflow工作流一个 .github/workflows/*.yml 文件就是一个可配置的自动化流程
Event事件触发工作流的仓库活动(push、PR、schedule 等)
Job任务一组在同一运行器上按顺序执行的步骤;不同 job 默认并行
Step步骤job 内最小的执行单元:一条 shell 命令或一个 Action
Action动作可复用的扩展单元,封装常用操作(检出代码、装环境等)
Runner运行器执行 job 的虚拟机(GitHub 托管或自托管)

理解要点:job 内的 steps 按顺序执行、可以共享数据(同一台机器);job 之间互相独立、默认并行,用 needs 声明依赖。

3. workflow 文件结构:读懂流水线的”图纸”

3.1 文件位置与命名

工作流文件必须放在仓库根目录的固定文件夹中:

graph TD
  A["仓库根目录"] --> B[".github"]
  B --> C["workflows(固定目录名,不能改名)"]
  C --> D["ci.yml(每个 .yml 文件 = 一个独立工作流)"]
  C --> E["deploy.yml"]
  C --> F["nightly.yml"]

3.2 顶层结构总览

一个标准的 workflow 文件由三大部分组成:

name: CI                    # 1. 工作流名称(显示在 Actions 页面)

on:                         # 2. 触发条件(什么时候跑)
  push:
    branches: [main]

permissions:                # (可选)最小权限声明
  contents: read

jobs:                       # 3. 任务集合(要干什么)
  build:                    #   job 标识
    runs-on: ubuntu-latest  #   在什么机器上跑
    steps:                  #   步骤列表(按顺序执行)
      - uses: actions/checkout@v6
      - run: npm ci

3.3 name 与 on

name: CI                    # 页面展示名,建议起名清晰(如 "Build and Test")
on: [push, pull_request]    # 简写:多个事件

on 的详细配置(分支过滤、路径过滤、定时、手动触发)见《Actions 触发器》(030),这里不展开。

3.4 jobs:任务编排

jobs:
  lint:                      # job 1:静态检查
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm run lint

  test:                      # job 2:测试(依赖 lint 完成)
    needs: lint              # 声明依赖:lint 成功后才跑 test
    runs-on: ubuntu-latest
    strategy:                # 矩阵:多版本并行测试
      matrix:
        node-version: [20, 22]   # 22 为当前活跃 LTS(18 已于 2025 年停止维护)
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci && npm test

  deploy:                    # job 3:部署(依赖 test)
    needs: test
    if: github.ref == 'refs/heads/main'   # 仅 main 分支部署
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

job 关键字段速查:

字段作用
runs-on指定运行器(ubuntu-latest / [self-hosted, linux])
needs依赖其他 job,串行化
strategy.matrix矩阵并行(见 032)
if条件执行
timeout-minutes超时控制(默认 360 分钟)
continue-on-error失败不阻断(实验性任务常用)
envjob 级环境变量

3.5 steps:步骤详解

step 只有两种形态:运行命令(run)或 调用 Action(uses)。

steps:
  # 形态一:调用市场 Action
  - name: Checkout code
    uses: actions/checkout@v6
    with:                    # 给 Action 传参
      fetch-depth: 0

  # 形态二:运行 shell 命令
  - name: Install dependencies
    run: |
      npm ci
      npm run build

  # 条件步骤
  - name: Deploy
    if: github.ref == 'refs/heads/main'
    run: ./deploy.sh

  # 环境变量(步骤级)
  - name: Print version
    run: echo "VERSION=$VERSION"
    env:
      VERSION: 1.0.0

4. Actions 市场:站在巨人肩膀上

4.1 在哪里找 Action

GitHub Marketplace(https://github.com/marketplace?type=actions)是官方 Action 市场,也可以在 uses: owner/repo@版本 中直接引用任意公开仓库的 Action。

4.2 高频 Action 清单(新手必备)

Action用途
actions/checkout检出仓库代码(几乎每个工作流第一步)
actions/setup-node配置 Node.js 环境
actions/setup-python配置 Python 环境
actions/setup-java配置 JDK(如 temurin)
actions/cache缓存依赖加速(见 033)
actions/upload-artifact上传构建产物(见 035)
actions/download-artifact下载构建产物(见 035)
peaceiris/actions-gh-pages部署静态站到 GitHub Pages
docker/login-action登录容器镜像仓库
docker/build-push-action构建并推送 Docker 镜像

4.3 版本固定:安全第一

Action 用 @版本 引用,建议固定主版本号(@v6)甚至提交 SHA(@a1b2c3d...):

- uses: actions/checkout@v6          # 主版本:随 v6.x 自动更新(推荐)
# - uses: actions/checkout@<完整SHA> # 最高安全:完全锁定代码

固定到 SHA 是官方安全加固建议——第三方 Action 若被篡改,固定 SHA 可避免意外执行恶意版本。

引用版本时还要注意运行时兼容性:JS Action 跑在 runner 内置的 Node.js 运行时上。2025 年起官方 Action 陆续升级到 Node 24 运行时(要求较新的 runner 版本),2026 年中 Node 20 运行时已进入弃用流程。使用较旧的大版本(如 checkout@v4)会随官方运行时退役而逐步失去维护,升级示例:

Actionv4/v5 时代当前主线(2026-09)说明
actions/checkout@v4(Node 20)@v6 / @v7v7 起 pull_request_target 默认禁止检出 fork PR 代码(安全加固),需要旧行为需显式声明 allow-unsafe-pr-checkout
actions/setup-node@v4(Node 20)@v6 / @v7v5 起自动读取 packageManager 字段启用缓存;v6 起自动缓存仅限 npm
actions/setup-python@v5(Node 20)@v6 / @v7v6 起基于 Node 24 运行时
actions/cache@v3/@v4@v5语义不变,运行时升级
actions/upload-artifact@v3(已被移除)@v4 / 更高v4 是大版本重构(见 035),v6+ 基于 Node 24

经验法则:新项目一律写当前主版本;维护老项目升级前看该 Action 的 Release Notes,尤其是标注 breaking 的变更。

5. 完整 CI/CD 示例:三种语言的流水线

5.1 Node.js 完整流水线(lint → test → build → deploy)

name: Node.js CI/CD
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [20, 22]        # 多版本测试
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with: { node-version: ${{ matrix.node-version }}, cache: npm }
      - run: npm ci
      - run: npm test

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4   # 产物上传,供部署用
        with: { name: build, path: dist/ }

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'    # 仅 main 分支部署
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with: { name: build, path: dist/ }
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

5.2 Java(Maven)流水线

name: Java CI/CD
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
          cache: maven                 # 内置 Maven 缓存
      - run: mvn -B package --file pom.xml
      - uses: actions/upload-artifact@v4
        with: { name: jar, path: target/*.jar }

5.3 Python 流水线

name: Python CI/CD
on:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with: { python-version: '3.11' }
      - run: |
          python -m pip install --upgrade pip
          pip install pytest
          pip install -r requirements.txt || true
      - run: pytest

6. 环境变量与密钥管理

6.1 环境变量(env)

支持工作流级、job 级、step 级三层:

env:                          # 工作流级
  NODE_ENV: production

jobs:
  build:
    env:                      # job 级
      BUILD_VERSION: 1.0.0
    steps:
      - name: Print env
        run: |
          echo "$NODE_ENV / $BUILD_VERSION"
          echo "分支: ${{ github.ref }}"     # 上下文变量

6.2 密钥(Secrets)

  • 仓库级 secrets:Settings → Secrets and variables → Actions,所有工作流可用。
  • 环境级 secrets:环境设置里配置,更安全(见 036)。
  • 使用方式:${{ secrets.XXX }},日志中自动打码。
steps:
  - name: Deploy
    run: ./deploy.sh
    env:
      API_KEY: ${{ secrets.API_KEY }}       # 不要硬编码密钥

7. 最佳实践清单

7.1 结构设计

  • 一个仓库多个工作流:CI、部署、定时任务拆开,互不影响。
  • needs 明确依赖:能并行的 job 就并行,需要结果的用 needs 串行。
  • if 控制分支:构建/测试全分支跑,部署只 main 跑。
  • 路径过滤:只改文档时不触发 CI(见 030 的 paths-ignore)。

7.2 安全

  • 最小权限:用 permissions 声明只读默认,按需放开:
permissions:
  contents: read
  pull-requests: write
  • 密钥入库:所有密钥放 Secrets,代码里绝不硬编码。
  • 固定版本:Action 固定主版本或 SHA;优先使用官方 actions/* 与已验证作者(verified creator)发布的 Action。
  • 开启 CodeQL:集成代码扫描(见 019 篇)。
  • 云部署用 OIDC 代替长期密钥:工作流可向云厂商(AWS/Azure/GCP 等)申请短时身份令牌,免去把长期 Access Key 存进 Secrets——在 job 上声明 permissions: { id-token: write },由各云厂商的官方登录 Action 完成互换。
  • 公共工作流要防注入:github.event 中来自外部的字段(PR 标题、分支名、Issue 正文等)直接拼进 run: 存在被注入执行的风险,先经环境变量中转或校验后再用。

7.2.1 复用:可重用工作流(reusable workflows)

同一套 CI 要在几十个仓库里复制粘贴?把工作流声明为可重用(workflow_call 触发器),其他仓库用 uses: owner/repo/.github/workflows/ci.yml@v1 直接调用:

# 被调方:.github/workflows/ci.yml(声明 workflow_call,定义 inputs/secrets)
on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: '22'

# 调用方:任意仓库的工作流里当一个 job 来用
jobs:
  ci:
    uses: my-org/shared-workflows/.github/workflows/ci.yml@v1
    with:
      node-version: '22'
    secrets: inherit          # 透传调用方的 secrets

这是组织级”统一流水线”的标准做法:改一处、全仓库生效,配合分支保护实现流水线收敛治理。

7.3 性能

  • 缓存依赖:setup-node 内置 cache 或 actions/cache(见 033)。
  • 矩阵并行:多 OS / 多版本并行测试(见 032)。
  • 产物按需:制品设置合理保留期(见 035)。
  • 超时兜底:job 设 timeout-minutes,防止死循环烧分钟数。

7.4 可维护性

  • 工作流文件命名清晰(ci.yml、deploy.yml、nightly-security-scan.yml)。
  • 复杂逻辑加中文注释。
  • 使用 gh workflow list、gh run list 查看状态与历史(见 gh CLI 篇)。

8. 常见错误与对策

常见错误报错/现象原因解决办法
工作流不触发推了代码没反应on 写错、文件名不在 .github/workflows/、默认分支问题核对文件路径与 on 语法;确认已合入默认分支
YAML 缩进错误Invalid workflow file缩进不一致(GitHub 报错红叉)用空格缩进(禁用 Tab),检查层级
uses: xxx 找不到Unable to resolve action拼写/版本错误,或仓库不存在核对 owner/repo@版本,去 Marketplace 复制
密钥为空secrets 取不到值密钥名拼错、作用域不对(仓库级 vs 环境级)检查 Secrets 配置与 ${{ secrets.XXX }} 拼写
job 并行导致乱序部署先于测试完成未用 needs 声明依赖下游 job 加 needs: [lint, test]
私有仓库超分钟额度任务排队/被拒私有仓库有免费分钟限制用缓存/矩阵并行优化;或自托管运行器
只改文档也跑 CI浪费分钟数没做路径过滤加 paths-ignore: ['docs/**', '*.md']

10. 一句话记忆

GitHub Actions 是仓库内置的”智能工厂”:用 .github/workflows/*.yml 描述 name/on/jobs/steps,事件一响,流水线自动跑完构建、测试、部署,全程可观测、可复用、可控制。