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 版本,减去排除的那一组)。exclude 和 include 很有用:可以排除已知不支持的组合,也可以给某个特定组合追加额外步骤。矩阵跑得越多,覆盖率越高,但要注意 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