GitHub Actions 高级工作流:构建高效的自动化流水线

一套流水线从「能跑」到「跑得稳、跑得快」,中间往往隔着不少细节。刚上手 GitHub Actions 时,很多人写的是「push 触发、装依赖、跑测试」三段式,够用但很基础。等仓库变复杂——要跨多个 Node 版本测试、要分环境部署、要避免在代码里存长期密钥——就需要更高级的工作流模式。本文基于 GitHub 的 CI/CD 能力,讲几个能立刻用上的模式。

一、矩阵构建:一次配置,并行跑多版本

最浪费时间的场景之一是:测试只跑在一个环境上,结果 Windows 上才出现的 bug 在发版后才被发现。矩阵构建用一组配置自动生成多个并行 job,每个 job 跑一套组合:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 22
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test

上面这个配置会生成 8 个并行 job(3 种系统 × 3 个 Node 版本,减去排除的那一组)。excludeinclude 很有用:可以排除已知不支持的组合,也可以给某个特定组合追加额外步骤。矩阵跑得越多,覆盖率越高,但要注意 runner 分钟数是按 job 计费的,组合别盲目堆。

二、可重用工作流:把通用逻辑抽出来

「装依赖、跑 lint、构建」这类步骤在多个仓库里长得几乎一样。GitHub Actions 支持可重用工作流(reusable workflows),用 workflow_call 定义,其他仓库按需调用:

# .github/workflows/deploy.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      DEPLOY_TOKEN:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh
        env:
          TOKEN: ${{ secrets.DEPLOY_TOKEN }}

可重用工作流适合「多个仓库共享同一套流程」的场景;如果只是同一个仓库里的一组步骤,用 composite action 更轻。判断标准很简单:跨仓库复用选 reusable workflow,单仓库内复用选 composite action。

三、环境与审批:给生产加一道人工闸门

直接把代码部署到生产是很多事故的源头。Environment 把「部署到哪」变成一等公民,还可以配置保护规则:要求指定成员审批、绑定环境专属的 secret、展示部署 URL:

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - run: ./deploy-prod.sh

配合仓库的 branch protection(要求 PR、要求检查通过),生产部署就变成了「自动化到一半、人工确认最后一环」的流程。审批人和 secret 都是环境级的,staging 用的密钥不会泄漏到生产环境。

四、OIDC:告别长期密钥

很多部署脚本里躺着一个长期有效的云厂商密钥,一旦泄漏就是大事故。OpenID Connect(OIDC)允许 Actions 向云平台申请短期、一次性的凭据,代码里不需要存任何密钥:

jobs:
  deploy-to-aws:
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-actions
          aws-region: us-east-1
      - run: aws s3 sync ./dist s3://my-bucket

AWS、GCP、Azure 都支持这个模式。初期配置多一点(在云平台侧建好信任关系),换来的是「仓库里没有可窃取的长期密钥」这个长期收益。对中小团队来说,即使没有条件立刻上 OIDC,至少要做到「密钥只存在仓库 Secrets 里、绝不进代码和日志」。配合环境级的 secret 作用域,就算一个环境的密钥泄露,影响范围也能被限制住。

五、缓存与条件执行

  • 缓存依赖:用 actions/cache 缓存 npm/pip 依赖,setup-node 也有内置 cache 参数,首次构建后命中缓存,能把安装时间从几分钟压到几十秒。
  • 路径过滤:文档改了就不必跑全量测试,用 on.push.paths 只在相关目录变更时触发对应 job。
  • 条件执行if 里可以判断分支、标签、PR 合并状态,比如只有打 tag 时才触发发布 job。

除了上面这些,还有两个值得掌握的细节。一是并发控制:多个 PR 同时触发同一个工作流时,用 concurrency 让新运行取消旧的,避免部署队列越积越长;二是超时保护:给 job 加 timeout-minutes,防止某个步骤卡死白占 runner。把 concurrency: { group: deploy, cancel-in-progress: true } 写进部署工作流,几乎每个团队都会用到。

一个落地案例

一个前后端一体仓库,用矩阵跑 Node 18/20/22 的测试,PR 合并到 main 后构建镜像并部署到 staging,人工审批通过后再部署生产。整个流程没有一处在服务器上手动操作,安全扫描也在每次提交时自动跑。一次配置后,团队每天几次发布都不需要任何人守在电脑前。整套流水线里,测试、构建、部署、扫描都是代码化的,任何人改配置都走 PR 评审,出了事故也能从日志里快速定位是哪次变更引起的。

常见问题

  • 矩阵 job 太多导致账单上涨? 合并相同配置、用 include 只保留真正需要的组合,必要时把低价值组合从默认触发里去掉。
  • secrets 在 fork 的 PR 里不可用? 这是安全设计。fork 的 PR 默认不注入 secrets,避免恶意代码偷密钥;需要时手动在 Actions 界面批准。
  • 工作流一直排队? 看看并发限制和 runner 类型,self-hosted runner 可以自己控制规模。
  • 部署成功但线上还是旧版本? 检查 CDN 缓存和部署产物是否真的更新(对比文件 hash),以及工作流里是否漏了缓存失效的步骤。

参考:GitHub Actions 官方文档 https://docs.github.com/actions ;可重用工作流 https://docs.github.com/actions/using-workflows/reusing-workflows