路线图

10-GitHub Actions

星辉 2026-07-02 阅读 5 min 948 字 路线图
10-GitHub Actions 封面

GitHub Actions 是 GitHub 内置的 CI/CD 平台,2018 年上线后迅速成为 SaaS CI 的主流选择。本章讲它的核心概念:workflow YAML 结构、runner、marketplace、矩阵构建、缓存策略。

workflow YAML 结构#

GitHub Actions 的配置文件放在仓库的 .github/workflows/ 目录,YAML 格式:

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# 仓库级权限
permissions:
  contents: read
  packages: write

# 环境变量
env:
  GO_VERSION: "1.22"
  REGISTRY: ghcr.io

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0    # 完整历史,给覆盖率工具用

      - uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true        # 自动缓存 Go modules

      - name: Run tests
        run: |
          go test -race -coverprofile=coverage.out ./...
          go tool cover -func=coverage.out | tail -1

      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage.out

  build:
    needs: test              # 依赖 test job 通过
    runs-on: ubuntu-latest
    if: github.event_name == 'push'   # PR 不构建镜像
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ github.repository }}:latest
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache,mode=max

关键字段#

  • on:触发条件(push/pull_request/schedule/workflow_dispatch)
  • jobs:定义一组 job,默认并行
  • needs:job 间依赖,串行执行
  • runs-on:runner 类型(ubuntu-latest/macos-latest/self-hosted)
  • steps:job 内的步骤,可以是 uses(调 Action)或 run(执行命令)
  • if:条件控制,如 if: github.ref == 'refs/heads/main'
  • strategy.matrix:矩阵构建
  • env:环境变量

Runner#

Runner 是跑 job 的机器。三种类型:

托管 Runner(GitHub 提供)#

yaml
runs-on: ubuntu-latest    # 公开仓库:4 核、16GB 内存、14GB SSD;私有仓库:2 核、8GB
# 也可选 ubuntu-22.04 具体版本
# macos-latest(macOS,贵 10 倍)
# windows-latest(Windows)

特点:免运维、按分钟计费、每次 job 干净环境。公开仓库免费,私有仓库每月 2000 分钟免费额度。

不同规格的托管 runner:

Runner配置用途
ubuntu-latest4 核 / 16GB(私有仓库 2 核 / 8GB)默认选择
ubuntu-latest (larger)8/16/32/64 核重型构建
macos-latest3 核 / 7GB(M1,14GB SSD)iOS/macOS 构建
windows-latest4 核 / 16GBWindows 专属

larger runner 按分钟计费较贵,但对大型 Monorepo 的全量构建值得——10 分钟的构建压到 2 分钟,省下的开发者等待时间远超 runner 费用。

自托管 Runner(Self-hosted)#

私有仓库大量构建时成本高,自托管更划算:

yaml
runs-on: self-hosted
# 也可加 label
runs-on: [self-hosted, linux, x64, gpu]

部署自托管 runner:

bash
# GitHub 仓库 → Settings → Actions → Runners → New self-hosted runner
# 按提示下载、配置、注册
./config.sh --url https://github.com/org/repo --token <token>
./run.sh         # 前台运行
./svc.sh install  # 注册为系统服务

注意:自托管 runner 有安全风险——PR 来自外部贡献者时,恶意代码可能窃取 runner 上的密钥。生产环境用专门的机器、隔离网络、定期清理。

GitHub-hosted vs Self-hosted#

维度托管 Runner自托管 Runner
成本按分钟计费,私有仓库有免费额度机器成本 + 运维
维护免运维要装依赖、升级、清理
安全隔离干净需自己隔离
性能固定规格可定制(GPU、大内存)
适合小团队、公开仓库大团队、私有仓库、特殊硬件需求

Marketplace Action#

GitHub Marketplace 有数万个第三方 Action,直接 uses 调用:

yaml
steps:
  # 官方 Action
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: '20'
      cache: 'npm'

  # 第三方 Action
  - uses: actions/setup-go@v5
    with:
      go-version: '1.22'

  # 校验 PR title 符合 Conventional Commits
  - uses: amannn/action-semantic-pull-request@v5
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # 触发semantic-release自动发版
  - uses: cycjimmy/semantic-release-action@v4
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

版本固定:永远用 @v4 这种 tag 或 @<commit-sha> 固定版本,不要用 @main @master——上游被入侵会影响你的流水线。

自己写 Action#

复用逻辑可以封装成 Action。Composite Action 是最简单的形式:

yaml
# .github/actions/setup-go-env/action.yml
name: 'Setup Go Environment'
description: 'Setup Go with cache and tools'

inputs:
  go-version:
    description: 'Go version'
    required: true
    default: '1.22'

runs:
  using: 'composite'
  steps:
    - uses: actions/setup-go@v5
      with:
        go-version: ${{ inputs.go-version }}
        cache: true

    - name: Install tools
      shell: bash
      run: |
        go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
        go install mvdan.cc/gofumpt@latest

调用:

yaml
- uses: ./.github/actions/setup-go-env
  with:
    go-version: '1.22'

矩阵构建#

同一 job 在多环境并行:

yaml
jobs:
  test:
    strategy:
      fail-fast: false       # 一个失败不取消其他
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        go-version: ["1.21", "1.22", "1.23"]
        exclude:
          - os: windows-latest
            go-version: "1.21"   # 排除某组合
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}
      - run: go test ./...

这会并行跑 3 × 3 - 1 = 8 个 job。矩阵构建是测试多版本兼容性的标准做法。

环境与密钥管理#

Secrets#

yaml
# 仓库 Settings → Secrets and variables → Actions 添加 secrets
# 使用
steps:
  - name: Deploy
    env:
      DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
      AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
    run: ./deploy.sh

Secrets 在 log 中自动 mask,不会显示明文。但有字符限制——包含 + / = 等特殊字符的 secret 在某些 SDK 里会出问题,建议 Base64 编码后存。

Environment#

环境(Environment)用于不同部署目标,可配独立的 reviewer 和 secrets:

yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production    # 仓库 Settings → Environments 配 reviewers
    steps:
      - run: ./deploy.sh

部署到 production environment 时,会要求配置的 reviewer 审批。这是 PROD 部署的人工 gate。

OIDC(推荐替代长期 AK/SK)#

AWS、阿里云等支持 OIDC 联邦认证,CI 拿短期 token 而不是长期密钥:

yaml
permissions:
  id-token: write   # 必需

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-actions
          aws-region: us-west-2
      # 后续步骤用短期凭证访问 AWS

比存 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 安全得多——OIDC token 短期有效,且能限定 source repo 和 branch。

缓存策略#

依赖缓存#

yaml
# Go modules 自动缓存
- uses: actions/setup-go@v5
  with:
    go-version: '1.22'
    cache: true

# npm 缓存
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

# pip 缓存
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'

通用缓存#

yaml
- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/go-build
      ~/go/pkg/mod
    key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
    restore-keys: |
      ${{ runner.os }}-go-

key 用依赖文件 hash——go.sum 变了 key 就变,缓存自动失效。restore-keys 是 fallback,找不到精确 key 时用前缀匹配。

Docker 镜像缓存#

yaml
- uses: docker/setup-buildx-action@v3

- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=registry,ref=myapp:buildcache
    cache-to: type=registry,ref=myapp:buildcache,mode=max

cache-from / cache-to 用 registry 存缓存,跨 runner 共享。mode=max 把所有中间层都缓存(不只是最终阶段),对多阶段构建的命中率提升显著。

Reusable Workflow#

跨仓库复用整套流水线——这是 GitHub Actions 的"模板化"方案,相当于 GitLab CI 的 include:

yaml
# .github/workflows/_reusable-ci.yml(被复用的 workflow)
on:
  workflow_call:
    inputs:
      service:
        required: true
        type: string
    secrets:
      AWS_ROLE_ARN:
        required: true

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: build ${{ inputs.service }}

调用:

yaml
# 仓库的 .github/workflows/release.yml
jobs:
  ci:
    uses: my-org/.github/.github/workflows/_reusable-ci.yml@v1
    with:
      service: user-service
    secrets: inherit

这是组织级 CI 模板化的标准方案——把所有服务共用的流水线抽到 .github 仓库,业务仓库只 uses: ... 一行调用。改模板时只改 .github 仓库,所有引用方自动生效(如果用 @v1 tag 则需要重新打 tag 才生效)。

Reusable Workflow vs Composite Action#

维度Reusable WorkflowComposite Action
粒度整个 job一组 step
能定义 job 级配置✓(runs-on、needs、environment)✗
能用 secrets✓(显式声明)✓(自动继承)
适合整套流水线复用单个步骤封装

通常组合使用:Composite Action 封装"安装 Go + 装 lint 工具"这种 step 级复用,Reusable Workflow 封装"构建 + 测试 + 部署"整套流水线。

实战要点#

  • 用 @vN tag 固定 Action 版本,不要用 @main。供应链安全的基本要求。
  • PR 触发不要给 fork 仓库 secrets。CI 用默认的 pull_request 事件即可——fork PR 天然拿不到 secrets。pull_request_target 恰恰相反(带 base 仓库 secrets + 写权限运行),只在确需 secrets 时用,且绝不 checkout 执行 PR 代码,并加 if: github.event.pull_request.head.repo.full_name == github.repository 检查。
  • PROD 部署用 environment + reviewer。这是 GitHub Actions 内置的人工 gate,比脚本里写 read -p 强得多。
  • OIDC 替代长期密钥。AWS/GCP/阿里云都支持,配置一次长期受益。
  • Reusable Workflow 做组织级模板。避免每个仓库重复写流水线,改一处全组织生效。
  • fail-fast: false 让矩阵跑完所有组合。某个版本失败时其他版本继续跑,便于一次看完所有问题。

小结#

GitHub Actions 的优势是生态(Marketplace 几万个 Action)+ 与 GitHub 深度集成(PR check、environment、OIDC)。SaaS 模式免运维,适合开源项目和小到中型团队。大团队私有仓库可以用 self-hosted runner 降成本。

下一章讲 GitLab CI——私有化和一体化场景的更优选。