前言
GitHub Actions 是 GitHub 原生的 CI/CD 平台,自 2019 年正式发布以来,已成为 DevOps 领域最受欢迎的自动化工具之一。它直接集成在代码仓库中,无需额外搭建 Jenkins 或 GitLab CI,就能实现从代码提交到生产部署的全流程自动化。
本篇笔记系统梳理 GitHub Actions 的核心概念、语法细节、常用场景和最佳实践,涵盖从入门到进阶的完整知识体系。所有配置示例均来自实际生产环境,可直接复用。
一、GitHub Actions 基础
1.1 核心概念
理解 GitHub Actions,先搞清楚这几个核心概念:
Workflow(工作流):一个自动化流程的定义文件,存放在
.github/workflows/目录下,使用 YAML 格式。Event(事件):触发 Workflow 的动作,比如
push、pull_request、schedule、workflow_dispatch等。Job(作业):Workflow 中的一个独立执行单元,由多个 Step 组成。多个 Job 默认并行执行,也可以用
needs依赖关系控制串行。Step(步骤):Job 中的单个任务,可以是运行一条 Shell 命令(
run),也可以是调用一个 Action(uses)。Action(动作):可复用的步骤单元,可以来自 GitHub Marketplace,也可以自己编写。
Runner(运行器):执行 Job 的机器。GitHub 提供托管 Runner(ubuntu-latest、windows-latest 等),也支持自托管 Runner。
Artifact(制品):Job 运行过程中产生的文件,可以在 Job 之间传递,也可以下载保存。
它们的关系是:一个 Workflow 文件定义了完整的流程 → 响应某个 Event 触发 → 包含一个或多个 Job → 每个 Job 由多个 Step 组成 → Step 在 Runner 上执行。
1.2 Workflow 文件结构
一个标准的 Workflow 文件长这样:
# .github/workflows/ci.yml
name: CI Pipeline # Workflow 名称,显示在 GitHub Actions 页面
on: # 触发条件
push:
branches: [main, develop]
pull_request:
branches: [main]
env: # 全局环境变量
NODE_VERSION: '20'
REGISTRY: ghcr.io
jobs: # 作业定义
build:
name: Build & Test
runs-on: ubuntu-latest # 运行环境
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test1.3 触发机制详解
on 字段定义了 Workflow 何时被触发,支持多种事件类型:
on:
# 1. Push 事件 —— 推送代码到指定分支时触发
push:
branches: [main, 'release/**']
tags: ['v*']
paths: ['src/**', 'package.json'] # 只有这些路径的文件变更才触发
paths-ignore: ['docs/**', '*.md'] # 排除这些路径
# 2. Pull Request 事件
pull_request:
types: [opened, synchronize, reopened] # PR 状态变更时触发
branches: [main]
# 3. 定时任务(Cron 表达式,UTC 时间)
schedule:
- cron: '0 2 * * 1' # 每周一 UTC 02:00(北京时间 10:00)
# 4. 手动触发
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options: [dev, staging, prod]
dry_run:
description: '模拟运行'
required: false
type: boolean
default: false
# 5. 其他 Workflow 完成后触发
workflow_run:
workflows: ["Build"]
types: [completed]
# 6. 当收到 issue comment 时触发(常用于 ChatOps)
issue_comment:
types: [created]实用技巧:paths 过滤在 monorepo 中特别有用,可以避免不相关的代码变更触发不必要的 CI。
二、Workflow 语法详解
2.1 Jobs 与依赖关系
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: echo "Linting..."
test:
runs-on: ubuntu-latest
needs: lint # lint 完成后才执行
steps:
- run: echo "Testing..."
deploy:
runs-on: ubuntu-latest
needs: [lint, test] # 两个都完成后才执行
if: github.ref == 'refs/heads/main' # 仅 main 分支部署
steps:
- run: echo "Deploying..."条件执行:if 表达式支持 GitHub Actions 的上下文变量和函数:
steps:
- name: Only on main
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: echo "Deploy to production"
- name: Skip for bot PRs
if: "!contains(github.event.pull_request.title, '[bot]')"
run: echo "Run tests"
- name: Continue on failure
if: failure() # 前一步失败时执行
run: echo "Handling failure..."
- name: Always run
if: always() # 无论前面成功失败都执行
run: echo "Cleanup"2.2 Steps 详解
每个 Step 可以有以下属性:
steps:
- name: Step name # 显示名称
id: my_step # 唯一 ID,用于后续引用输出
if: success() # 条件执行
working-directory: ./src # 工作目录
env: # Step 级别的环境变量
MY_VAR: 'hello'
run: | # Shell 命令
echo "Value: $MY_VAR"
echo "output=value" >> $GITHUB_OUTPUT # 设置输出
continue-on-error: true # 允许失败而不中断
- name: Use previous output
run: echo "Got ${{ steps.my_step.outputs.output }}"2.3 Matrix 策略
Matrix 让你用一份配置跑多组参数,非常适合多版本、多平台测试:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
exclude: # 排除特定组合
- os: windows-latest
node-version: 18
include: # 追加额外组合
- os: ubuntu-latest
node-version: 22
experimental: true
fail-fast: false # 一个失败不取消其他
max-parallel: 3 # 最多并行 3 个
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci && npm test
- name: Notify experimental failure
if: failure() && matrix.experimental
run: echo "Experimental build failed, not blocking"2.4 环境变量与 Secrets
env:
GLOBAL_VAR: 'available to all jobs'
jobs:
build:
runs-on: ubuntu-latest
env:
JOB_VAR: 'available to all steps in this job'
steps:
- name: Use env
env:
STEP_VAR: 'available to this step only'
run: |
echo "Global: $GLOBAL_VAR"
echo "Job: $JOB_VAR"
echo "Step: $STEP_VAR"
echo "Secret: ${{ secrets.MY_SECRET }}"
echo "Env from context: ${{ vars.MY_CONFIG }}"注意:Secrets 在日志中会被自动遮蔽为 ***,但要小心不要通过 echo 或其他方式间接泄露。使用 env 传入环境变量比直接在 run 中引用更安全。
三、常用 Action
3.1 actions/checkout
几乎所有 Workflow 都需要的第一步:
- uses: actions/checkout@v4
with:
repository: owner/repo # 检出其他仓库
ref: develop # 指定分支/标签
token: ${{ secrets.GH_PAT }} # 使用 PAT 访问私有仓库
submodules: true # 递归检出子模块
fetch-depth: 0 # 完整历史(用于 changelog 生成)
path: my-repo # 检出到指定目录3.2 actions/setup-node
- uses: actions/setup-node@v4
with:
node-version: '20' # 支持 semver,如 '20.x'、'^20'
cache: 'npm' # 自动缓存 npm/pnpm/yarn 依赖
registry-url: 'https://registry.npmjs.org'
scope: '@my-org' # npm scope3.3 actions/setup-java
- uses: actions/setup-java@v4
with:
distribution: 'temurin' # temurin/zulu/adopt/microsoft/liberica/corretto
java-version: '21'
cache: 'gradle' # 缓存 Gradle 或 Maven 依赖
# cache: 'maven'
server-id: github # Maven settings.xml 配置
settings-path: ${{ github.workspace }}3.4 actions/setup-python
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
# cache: 'pipenv'
# cache: 'poetry'3.5 docker/build-push-action
构建并推送 Docker 镜像:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64 # 多架构构建3.6 其他常用 Action
# 缓存任意目录
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
# 上传制品
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7
# 下载制品
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
# 发送 Slack 通知
- uses: slackapi/slack-github-action@v2
with:
webhook: ${{ secrets.SLACK_WEBHOOK }}
webhook-type: incoming-webhook
payload: |
{"text": "Build ${{ job.status }}: ${{ github.repository }}"}四、自托管 Runner
当项目需要特殊硬件、内网访问、或自定义环境时,自托管 Runner 是必要选择。
4.1 安装与配置
# 创建目录
mkdir actions-runner && cd actions-runner
# 下载最新版本(Linux x64 为例)
curl -o actions-runner-linux-x64-2.319.1.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.319.1/actions-runner-linux-x64-2.319.1.tar.gz
tar xzf actions-runner-linux-x64-2.319.1.tar.gz
# 配置(从 GitHub 仓库 Settings > Actions > Runners 获取 token)
./config.sh --url https://github.com/your-org/your-repo \
--token YOUR_REGISTRATION_TOKEN \
--labels self-hosted,linux,x64,gpu \
--name prod-runner-01 \
--work _work
# 作为 systemd 服务安装运行
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status4.2 标签管理
标签决定了哪些 Job 会被分配到哪个 Runner:
jobs:
# 使用 GitHub 托管 Runner
lint:
runs-on: ubuntu-latest
# 使用自托管 Runner(必须有所有指定标签)
build:
runs-on: [self-hosted, linux, gpu]
# 使用标签做更细粒度的匹配
deploy:
runs-on: [self-hosted, linux, production]4.3 Runner 分组(Runner Group)
对于大型团队,可以用 Runner Group 实现权限隔离:
jobs:
deploy:
runs-on: [self-hosted, linux, production]
# 在仓库/组织设置中配置 Runner Group 的访问控制4.4 安全考虑
自托管 Runner 的安全至关重要:
# 对于公开仓库,强烈建议仅使用临时 Runner
jobs:
build:
runs-on: [self-hosted, linux]
container:
image: node:20-slim # 在容器中运行,隔离环境
options: --read-only # 只读文件系统
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test安全清单:
不要在公开仓库使用持久化的自托管 Runner(PR 可能包含恶意代码)
使用容器模式运行 Job,限制资源访问
定期更新 Runner 版本
使用
CODEOWNERS控制 Workflow 文件的修改权限为 Runner 设置专用的低权限用户
4.5 自动伸缩
使用 Actions Runner Controller (ARC) 在 Kubernetes 上实现 Runner 自动伸缩:
# ARC Helm values 示例
# helm install arc --namespace arc-systems \
# actions-runner-controller/actions-runner-controller \
# -f arc-values.yaml
# arc-values.yaml
authSecret:
create: true
github_token: "ghp_xxxxxxxxxxxx"
runnerDeployment:
name: org-runner
replicas: 2
spec:
organization: your-org
labels:
- k8s
- linux
resources:
limits:
cpu: "2"
memory: "4Gi"
# HorizontalRunnerAutoscaler 配置
horizontalRunnerAutoscaler:
minReplicas: 1
maxReplicas: 10
scaleDownDelaySecondsAfterScaleOut: 300
metrics:
- type: PercentageRunnersBusy
scaleUpThreshold: '0.75'
scaleDownThreshold: '0.25'五、Secrets 管理
5.1 Secrets 层级
GitHub Actions 的 Secrets 分三个层级,优先级从高到低:
Environment Secrets — 绑定到特定部署环境(如 production),可以配合审批流程
Repository Secrets — 仓库级别,所有 Workflow 可访问
Organization Secrets — 组织级别,可共享给多个仓库
steps:
- name: Use secrets
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Repository secret
DEPLOY_KEY: ${{ secrets.PROD_DEPLOY_KEY }} # Environment secret
SHARED_API_KEY: ${{ secrets.ORG_API_KEY }} # Organization secret
run: |
echo "Deploying with key..."
# 不要 echo secret 值5.2 配置 Environment Secrets
在 GitHub 仓库的 Settings → Environments 中配置:
# 使用 Environment
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # 引用 environment 名称
steps:
- name: Deploy
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: ./deploy.sh5.3 OIDC 联合身份认证(推荐)
OIDC 无需存储长期凭据,是目前最安全的方式:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # 必须声明 id-token 权限
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: ap-southeast-1
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket
# 同样支持 GCP、Azure
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github/providers/github
service_account: github-actions@my-project.iam.gserviceaccount.comOIDC 配置步骤:
在云平台创建身份提供商(Identity Provider),指向 GitHub 的 OIDC 端点
创建角色并配置信任策略,仅允许特定仓库/分支的 Workflow 假设角色
在 Workflow 中使用对应的 Action 获取临时凭据
5.4 变量(Variables)vs Secrets
非敏感配置建议使用 Variables 而非 Secrets:
# Repository/Environment Variables(非敏感,日志中可见)
env:
APP_URL: ${{ vars.APP_URL }}
FEATURE_FLAG: ${{ vars.ENABLE_NEW_UI }}
# Secrets(敏感,日志中被遮蔽)
env:
API_KEY: ${{ secrets.API_KEY }}六、常用场景实战
6.1 Node.js CI 完整配置
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
ci:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Unit tests
run: npm test -- --coverage
- name: Upload coverage
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
- name: Build
run: npm run build
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: build
path: dist/
retention-days: 36.2 Java CI 完整配置(Gradle)
name: Java CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build
run: ./gradlew build -x test
- name: Test
run: ./gradlew test
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: build/reports/tests/
- name: Upload JAR
uses: actions/upload-artifact@v4
with:
name: app-jar
path: build/libs/*.jar6.3 Docker 构建与推送
name: Docker Build & Push
on:
push:
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm646.4 自动发布 npm 包
name: Release
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build
- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: dist/*七、多环境部署
7.1 环境定义与审批流程
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options: [staging, production]
jobs:
deploy-dev:
runs-on: ubuntu-latest
environment: dev
steps:
- uses: actions/checkout@v4
- name: Deploy to dev
run: |
echo "Deploying to dev..."
# 实际部署命令
deploy-staging:
runs-on: ubuntu-latest
needs: deploy-dev
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: echo "Deploying to staging..."
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to production
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: echo "Deploying to production..."7.2 环境保护规则
在 GitHub 仓库 Settings → Environments 中配置:
Required reviewers:指定审批人,部署前必须有人批准
Wait timer:部署前等待指定分钟数(可用于"冷静期")
Deployment branches:限制只有特定分支可以部署到该环境
Environment Secrets / Variables:环境级别的配置和凭据
7.3 分支策略与自动部署
name: Multi-env Deploy
on:
push:
branches:
- develop # → dev 环境
- 'release/*' # → staging 环境
- main # → production 环境
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref_name == 'develop' && 'dev' || startsWith(github.ref_name, 'release/') && 'staging' || 'production' }}
steps:
- uses: actions/checkout@v4
- name: Determine env
id: env
run: |
if [[ "${{ github.ref_name }}" == "develop" ]]; then
echo "target=dev" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref_name }}" == release/* ]]; then
echo "target=staging" >> $GITHUB_OUTPUT
else
echo "target=production" >> $GITHUB_OUTPUT
fi
- name: Deploy to ${{ steps.env.outputs.target }}
run: echo "Deploying to ${{ steps.env.outputs.target }}"八、复合 Action 与可重用 Workflow
8.1 复合 Action(Composite Action)
复合 Action 将多个步骤打包成一个可复用的单元,存放在仓库中:
# .github/actions/setup-project/action.yml
name: 'Setup Project'
description: 'Install dependencies and setup environment'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
install-command:
description: 'Install command'
required: false
default: 'npm ci'
runs:
using: 'composite'
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- name: Install dependencies
shell: bash
run: ${{ inputs.install-command }}
- name: Verify setup
shell: bash
run: |
echo "Node: $(node -v)"
echo "npm: $(npm -v)"使用复合 Action:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
with:
node-version: '22'
- run: npm test8.2 可重用 Workflow(Reusable Workflow)
可重用 Workflow 通过 workflow_call 触发,适合跨仓库或跨项目的标准化流程:
# .github/workflows/reusable-deploy.yml(可重用 Workflow)
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
replicas:
required: false
type: number
default: 2
secrets:
DEPLOY_TOKEN:
required: true
outputs:
deploy-url:
description: "Deployment URL"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- name: Deploy
id: deploy
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"
echo "Replicas: ${{ inputs.replicas }}"
echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUT调用可重用 Workflow:
# .github/workflows/deploy.yml
name: Deploy Application
on:
push:
branches: [main]
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
image-tag: ${{ github.sha }}
replicas: 2
secrets:
DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
image-tag: ${{ github.sha }}
replicas: 5
secrets:
DEPLOY_TOKEN: ${{ secrets.PROD_DEPLOY_TOKEN }}跨仓库调用:
jobs:
ci:
uses: your-org/shared-workflows/.github/workflows/node-ci.yml@main
with:
node-version: '20'
secrets: inherit # 继承调用方的所有 secrets九、缓存优化
9.1 actions/cache 基础
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-9.2 内置缓存(推荐)
很多 setup Action 已内置缓存支持,无需手动配置 actions/cache:
# Node.js —— 缓存 npm/pnpm/yarn
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm' # 自动检测 lockfile 并缓存
# Java —— 缓存 Gradle/Maven
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: 'gradle'
# Python —— 缓存 pip/poetry/pipenv
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
# Go —— 使用 actions/cache
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# Rust —— 缓存 cargo
- uses: Swatinem/rust-cache@v29.3 Docker 构建缓存
# 方式一:使用 GitHub Actions 缓存后端(推荐)
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: my-app:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# 方式二:使用 Registry 缓存
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: my-app:latest
cache-from: type=registry,ref=ghcr.io/my-app:buildcache
cache-to: type=registry,ref=ghcr.io/my-app:buildcache,mode=max
# 方式三:使用本地缓存
- uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: ${{ runner.os }}-buildx-
- uses: docker/build-push-action@v6
with:
context: .
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
# 防止缓存无限增长
- run: rm -rf /tmp/.buildx-cache && mv /tmp/.buildx-cache-new /tmp/.buildx-cache9.4 缓存最佳实践
Key 设计:使用
hashFiles()基于 lockfile 生成 key,确保依赖变化时缓存失效restore-keys:设置回退 key,即使精确匹配失败也能用到近似的旧缓存
缓存大小:GitHub 托管 Runner 每个仓库有 10GB 缓存上限,超出会自动清理最旧的
跨分支:缓存在同一仓库的分支间共享,
main分支的缓存可以被 PR 分支读取
十、最佳实践
10.1 安全
# 1. 最小权限原则
permissions:
contents: read # 仅读取代码
# 不要写 permissions: write-all
# 2. Pin Action 版本(避免供应链攻击)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# 或至少用主版本号
# uses: actions/checkout@v4
# 3. 不要在日志中暴露敏感信息
- name: Safe logging
run: |
echo "::add-mask::$MY_SECRET" # 手动遮蔽
echo "Token length: ${#MY_SECRET}" # 只打印长度
# 4. 使用 GITHUB_TOKEN 而非 PAT(自动过期,权限有限)
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
# 5. 验证第三方 Action 的来源
# 优先使用 GitHub 官方 Action 和知名组织的 Action10.2 性能优化
# 1. 使用 Job 级别的 timeout
jobs:
build:
timeout-minutes: 15
# 2. 合理使用 needs 做并行
jobs:
lint:
runs-on: ubuntu-latest
steps: [...]
test:
runs-on: ubuntu-latest # 和 lint 并行
steps: [...]
build:
needs: [lint, test] # lint 和 test 都完成后
steps: [...]
# 3. 条件跳过不必要的 Job
jobs:
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps: [...]
# 4. 合并多个小 Step 减少启动开销
- name: Setup and test
run: |
npm ci
npm run lint
npm test
npm run build
# 5. 使用 paths 过滤避免不必要的触发
on:
push:
paths: ['src/**', 'tests/**', 'package.json', 'package-lock.json']10.3 调试技巧
# 1. 启用调试日志(在仓库 Settings > Secrets 中设置)
# ACTIONS_STEP_DEBUG = true
# ACTIONS_RUNNER_DEBUG = true
# 2. 使用 act 在本地测试(https://github.com/nektos/act)
# act -j build
# 3. 打印上下文信息
- name: Debug context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
JOB_CONTEXT: ${{ toJson(job) }}
STEPS_CONTEXT: ${{ toJson(steps) }}
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: |
echo "Event: ${{ github.event_name }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
echo "Runner OS: ${{ runner.os }}"
# 4. tmate 远程调试(暂停 Workflow 并提供 SSH 访问)
- name: Setup tmate session
if: failure() # 仅在失败时启动
uses: mxschmitt/action-tmate@v3
# 或者用 workflow_dispatch 手动触发调试10.4 成本控制
GitHub Actions 定价要点:
公开仓库免费
私有仓库每月有免费额度(GitHub Free: 2000 分钟/月,Pro: 3000 分钟/月)
不同 Runner 的系数不同:Linux = 1x,macOS = 10x,Windows = 2x
成本优化策略:
# 1. 使用 paths 过滤
on:
push:
paths: ['src/**'] # 只有源码变更才触发
# 2. 及时取消过时的 Workflow
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 同一分支的新提交会取消旧的运行
# 3. 使用小型 Runner(按需选择)
runs-on: ubuntu-latest # Linux 最便宜
# runs-on: macos-latest # macOS 贵 10 倍
# 4. 缓存依赖,减少安装时间
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
# 5. 精简 CI 步骤
# - 跳过 lint 如果有本地 pre-commit hook
# - 只在 PR 时跑完整测试,push 时只跑核心测试
# 6. 合并多个小 Workflow
# - 避免同一个事件触发多个 Workflow
# - 使用 reusable workflow 减少重复配置10.5 维护性
# 1. 使用 YAML 锚点减少重复(GitHub Actions 不原生支持,但可以用 reusable workflow 替代)
# 2. 统一 Action 版本管理(使用 Dependabot)
# .github/dependabot.yml
# version: 2
# updates:
# - package-ecosystem: "github-actions"
# directory: "/"
# schedule:
# interval: "weekly"
# 3. 使用 Matrix 测试多版本
strategy:
matrix:
node: [18, 20, 22]
# 4. 为每个 Job 设置有意义的 name
jobs:
test:
name: "Test (Node ${{ matrix.node }})"
# 5. 使用 Workflow 链接相关 PR
- name: Add PR comment
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `✅ Build passed! [View logs](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`
})附录:常见问题速查
Q: Workflow 文件在哪里? A: .github/workflows/ 目录下,YAML 格式,文件名任意。
Q: 如何手动触发 Workflow? A: 在 on 中添加 workflow_dispatch,然后在 GitHub Actions 页面点击 "Run workflow"。
Q: Secrets 有大小限制吗? A: 单个 Secret 最大 64KB,每个仓库最多 100 个,组织最多 1000 个。
Q: 如何在 Job 之间传递文件? A: 使用 actions/upload-artifact 和 actions/download-artifact。
Q: Cron 表达式是哪个时区? A: UTC。北京时间要减 8 小时,比如北京时间 10:00 = UTC 02:00。
Q: 如何复用同一仓库的 Workflow? A: 使用 workflow_call 定义可重用 Workflow,然后用 uses: ./.github/workflows/xxx.yml 调用。
Q: 自托管 Runner 如何清理工作目录? A: Runner 默认在每次 Job 结束后清理工作目录。如果需要保留,可以在 Step 中使用 actions/cache。