
超越基础 GitHub Actions
大多数 GitHub Actions 教程都覆盖相同的内容:一个在推送时运行测试的工作流,可能还会构建 Docker 镜像。但生产流水线需要更多:基于环境的部署门控、安全的密钥管理、高效的缓存以及可维护的工作流组织。
本指南涵盖了使 GitHub Actions 能够大规模运行的模式。

环境和部署门控
环境为部署添加保护规则:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
deploy-staging:
needs: test
runs-on: ubuntu-latest
environment: staging # 使用 staging 环境密钥
steps:
- name: Deploy to staging
run: ./deploy.sh staging
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} # 来自 staging 环境
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # 可以要求手动审批
# 生产环境可以配置:
# - 必需审阅者(必须在作业运行前批准)
# - 等待计时器(部署前延迟)
# - 分支限制(仅来自 main)
steps:
- name: Deploy to production
run: ./deploy.sh production
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} # 来自 production 环境
在 GitHub Settings → Environments 中,配置:
- 必需审阅者:必须批准的有名人员
- 等待计时器:延迟(可用于金丝雀监控期)
- 部署分支:只有特定分支可以部署到此环境
OIDC 认证(无需长期密钥)
无需将云凭证存储为 GitHub 密钥,而是使用 OIDC 获取临时令牌:
name: Deploy with OIDC
on:
push:
branches: [main]
permissions:
id-token: write # OIDC 必需
contents: read
jobs:
deploy-aws:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 通过 OIDC 获取临时 AWS 凭证(无需存储 AWS 密钥!)
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: us-east-1
# 无需访问密钥或秘密密钥!
- name: Deploy to ECS
run: aws ecs update-service --cluster prod --service my-app --force-new-deployment
deploy-gcp:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/my-pool/providers/my-provider
service_account: deploy@my-project.iam.gserviceaccount.com
- run: gcloud run deploy my-service --image gcr.io/my-project/app:${{ github.sha }}

矩阵策略
跨多个组合运行作业:
jobs:
test:
strategy:
fail-fast: false # 失败时不取消其他矩阵作业
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [18, 20, 22]
exclude:
- os: macos-latest
node: 18 # 不在 macOS 上测试 Node 18
include:
- os: ubuntu-latest
node: 20
experimental: true # 为特定组合添加额外变量
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
build:
strategy:
matrix:
platform: [linux/amd64, linux/arm64] # 多架构 Docker 构建
steps:
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
platforms: ${{ matrix.platform }}
tags: my-app:${{ github.sha }}-${{ matrix.platform }}
缓存:让工作流变快
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Node.js — 缓存 node_modules
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # npm/yarn/pnpm 的内置缓存
- run: npm ci # 后续运行使用缓存
# 显式缓存以获得更多控制
- name: Cache Next.js build
uses: actions/cache@v4
with:
path: |
.next/cache
~/.npm
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
${{ runner.os }}-nextjs-
# 使用 GitHub Actions 缓存的 Docker 层缓存
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha # 从 GitHub Actions 缓存读取
cache-to: type=gha,mode=max # 写入 GitHub Actions 缓存
push: true
tags: my-app:${{ github.sha }}

可复用工作流
工作流的 DRY 原则——定义一次,从多个工作流调用:
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
secrets:
DEPLOY_TOKEN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Deploy ${{ inputs.image-tag }} to ${{ inputs.environment }}
run: |
curl -X POST https://deploy.example.com/deploy -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" -d '{"image": "${{ inputs.image-tag }}", "env": "${{ inputs.environment }}"}'
# .github/workflows/main.yml
name: Main Pipeline
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- id: meta
uses: docker/metadata-action@v5
with:
images: my-app
tags: type=sha
- uses: docker/build-push-action@v5
with:
tags: ${{ steps.meta.outputs.tags }}
push: true
deploy-staging:
needs: build
uses: ./.github/workflows/reusable-deploy.yml # 调用可复用工作流
with:
environment: staging
image-tag: ${{ needs.build.outputs.image-tag }}
secrets: inherit # 传递所有密钥
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
image-tag: ${{ needs.build.outputs.image-tag }}
secrets: inherit
复合操作
用于工作流内可复用的步骤序列:
# .github/actions/setup-app/action.yml
name: Setup Application
description: Install dependencies and build the app
inputs:
node-version:
description: Node.js version
default: '20'
environment:
description: Build environment
required: true
runs:
using: composite
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
shell: bash
- run: npm run build
shell: bash
env:
NODE_ENV: ${{ inputs.environment }}
- run: npm test -- --coverage
shell: bash
# 使用复合操作
- uses: ./.github/actions/setup-app
with:
node-version: '20'
environment: production
高级作业依赖和条件
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- run: npm test
build:
needs: [lint, test] # 等待两者都通过
if: github.ref == 'refs/heads/main' # 仅在 main 分支上
runs-on: ubuntu-latest
steps:
- run: npm run build
notify-failure:
needs: [build]
if: failure() # 如果任何先前作业失败则运行
runs-on: ubuntu-latest
steps:
- name: Notify team on Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🚨 Build failed: ${{ github.repository }}@${{ github.sha }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
cleanup:
needs: [build]
if: always() # 无论成功/失败都运行
runs-on: ubuntu-latest
steps:
- run: ./cleanup.sh
工作流安全最佳实践
# 最小权限原则
permissions:
contents: read # 默认——在作业中进一步限制
packages: write # 仅当推送到 ghcr.io 时
id-token: write # 仅当使用 OIDC 时
# 除非明确需要,否则不要授予 pull-requests: write
# 将操作版本固定到精确 SHA(而不是 @main 或 @v1)
# ❌ 易受供应链攻击:
- uses: some-action/action@main
# ✅ 固定到特定提交:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# 验证 PR 触发工作流的输入(不受信任的代码)
- name: Validate PR title
run: |
TITLE="${{ github.event.pull_request.title }}"
# 清理——不要在 shell 命令中直接使用 PR 内容
echo "PR title length: ${#TITLE}"
# 使用最小范围的 GITHUB_TOKEN
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
一个设计良好的 CI/CD 流水线在日常开发中是不可见的——它只是工作、运行快速、可靠部署。在 OIDC 认证、可复用工作流和适当缓存等模式上的投资,会在项目的数月和数年内带来回报。
→ 使用 UUID 生成器 为工作流中的唯一标识符生成 UUID。