GitHub Actions
GitHub Actions 是 GitHub 内置的 CI/CD 平台,通过在 .github/workflows/ 目录下放置 YAML 文件定义工作流,与代码仓库深度集成,支持 Push、PR、Issue、Release 等几乎所有 GitHub 事件触发。
核心概念:
| 概念 | 说明 |
|---|---|
| Workflow | 工作流,一个 YAML 文件定义一套自动化流程 |
| Event | 触发 Workflow 的事件(push、pull_request、schedule 等) |
| Job | 工作流中的一个任务,默认并行执行 |
| Step | Job 中的单个步骤,顺序执行 |
| Action | 可复用的步骤单元,来自 GitHub Marketplace 或自定义 |
| Runner | 执行 Job 的机器(GitHub 托管 或 Self-hosted) |
| Artifact | Job 产物,可跨 Job 或工作流共享 |
| Secret | 加密存储的敏感信息,通过 ${{ secrets.NAME }} 引用 |
| Variable | 非敏感配置变量,通过 ${{ vars.NAME }} 引用 |
| Environment | 部署目标环境,可配置保护规则和审批人 |
| Matrix | 矩阵策略,用不同参数组合并行运行同一 Job |
| Context | 运行时信息(github、env、secrets、jobs 等上下文) |
Workflow 基本结构
# .github/workflows/ci.yml
name: CI Pipeline # 工作流名称(显示在 Actions 页面)
on: # 触发事件
push:
branches: [main, develop]
pull_request:
branches: [main]
env: # 全局环境变量
APP_NAME: my-app
REGISTRY: ghcr.io
jobs:
build: # Job ID
name: Build Application # Job 显示名(可选)
runs-on: ubuntu-latest # Runner 类型
steps:
- name: Checkout code
uses: actions/checkout@v4 # 使用 Action
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Upload JAR
uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
retention-days: 7
触发事件(on)
代码事件
on:
# Push 触发
push:
branches:
- main
- "release/**"
branches-ignore:
- "docs/**"
paths: # 只在这些文件变更时触发
- "src/**"
- "pom.xml"
paths-ignore:
- "**.md"
tags:
- "v*.*.*"
# PR 触发
pull_request:
types: [opened, synchronize, reopened] # PR 事件类型
branches: [main]
# PR 合并到目标分支(用于 fork 仓库也能访问 secrets)
pull_request_target:
branches: [main]
定时触发
on:
schedule:
- cron: "0 2 * * 1-5" # 工作日凌晨 2 点(UTC)
- cron: "0 0 * * 0" # 每周日零点
# cron 使用 UTC 时间,国内换算 +8 小时
手动触发
on:
workflow_dispatch:
inputs:
environment:
description: "部署环境"
required: true
default: "staging"
type: choice
options: [dev, staging, prod]
version:
description: "版本号"
required: false
type: string
dry-run:
description: "演练模式"
default: false
type: boolean
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: echo "部署到 ${{ inputs.environment }},版本 ${{ inputs.version }}"
其他常用事件
on:
release:
types: [published] # 发布 Release 时触发
issues:
types: [opened, labeled]
workflow_call: # 被其他 Workflow 调用(可复用 Workflow)
inputs:
image-tag:
required: true
type: string
secrets:
deploy-token:
required: true
repository_dispatch: # 外部 API 触发
types: [deploy-event]
内置上下文与变量
常用上下文
steps:
- run: |
# github 上下文
echo "仓库:${{ github.repository }}" # org/repo
echo "分支:${{ github.ref_name }}"
echo "SHA:${{ github.sha }}"
echo "短SHA:${{ github.sha }}" | cut -c1-8
echo "事件:${{ github.event_name }}"
echo "Actor:${{ github.actor }}" # 触发者用户名
echo "Run ID:${{ github.run_id }}"
echo "Run Number:${{ github.run_number }}"
# runner 上下文
echo "OS:${{ runner.os }}"
echo "临时目录:${{ runner.temp }}"
# env 上下文
echo "APP_NAME:${{ env.APP_NAME }}"
常用预定义变量
| 变量 | 说明 |
|---|---|
$GITHUB_SHA |
完整 Commit SHA |
$GITHUB_REF |
完整 ref(refs/heads/main) |
$GITHUB_REF_NAME |
分支或 Tag 名 |
$GITHUB_REPOSITORY |
仓库名(owner/repo) |
$GITHUB_WORKSPACE |
工作目录路径 |
$GITHUB_RUN_ID |
Workflow 运行 ID |
$GITHUB_RUN_NUMBER |
Workflow 运行编号 |
$GITHUB_ACTOR |
触发用户 |
$GITHUB_EVENT_NAME |
触发事件名 |
$GITHUB_OUTPUT |
输出变量文件路径 |
$GITHUB_ENV |
环境变量文件路径 |
$RUNNER_TEMP |
Runner 临时目录 |
在 Step 间传递输出
steps:
- name: 设置版本号
id: version
run: |
VERSION=$(cat package.json | jq -r '.version')
echo "version=$VERSION" >> $GITHUB_OUTPUT # 写入输出
- name: 使用版本号
run: echo "版本是 ${{ steps.version.outputs.version }}"
- name: 设置环境变量
run: echo "IMAGE_TAG=v${{ steps.version.outputs.version }}" >> $GITHUB_ENV
- name: 使用环境变量
run: echo "$IMAGE_TAG"
Job 配置详解
needs(Job 依赖)
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: mvn package
test:
needs: build # 等待 build 完成后运行
runs-on: ubuntu-latest
steps:
- run: mvn test
deploy:
needs: [build, test] # 等待多个 Job
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
条件执行(if)
jobs:
deploy:
runs-on: ubuntu-latest
# Job 级别条件
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: 只在成功时部署
if: success()
run: ./deploy.sh
- name: 失败时通知
if: failure()
run: ./notify-failure.sh
- name: 始终运行(清理)
if: always()
run: ./cleanup.sh
- name: 条件表达式
if: contains(github.event.head_commit.message, '[deploy]')
run: ./deploy.sh
常用条件函数:
| 函数 | 说明 |
|---|---|
success() |
前面步骤全部成功 |
failure() |
前面有步骤失败 |
always() |
始终执行 |
cancelled() |
工作流被取消 |
contains(str, substr) |
字符串包含 |
startsWith(str, prefix) |
字符串前缀 |
endsWith(str, suffix) |
字符串后缀 |
format(str, ...) |
字符串格式化 |
join(array, sep) |
数组连接 |
toJSON(value) |
转 JSON |
fromJSON(str) |
解析 JSON |
hashFiles(pattern) |
计算文件哈希 |
matrix(矩阵策略)
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # 某个组合失败不取消其他
max-parallel: 4 # 最多并行 4 个
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: ["18", "20", "22"]
include: # 额外添加组合
- os: ubuntu-latest
node: "20"
experimental: true
exclude: # 排除某个组合
- os: windows-latest
node: "18"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
environment(部署环境保护)
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment:
name: production
url: https://app.example.com # 显示在 GitHub Deployments 页面
steps:
- run: ./deploy.sh prod
在 GitHub 上配置 Environment 保护规则:
- Settings → Environments → New environment
- Required reviewers:指定必须审批的人或团队
- Wait timer:部署前等待时间(分钟)
- Deployment branches:限定只有特定分支可以部署
concurrency(并发控制)
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 新推送自动取消旧的运行
# Job 级别并发控制
jobs:
deploy:
concurrency:
group: deploy-${{ inputs.environment }}
cancel-in-progress: false # 部署不取消,排队等待
timeout 与 retry
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30 # Job 超时
steps:
- name: 运行测试
timeout-minutes: 15 # Step 超时
continue-on-error: true # 步骤失败不终止 Job
run: npm test
- name: 重试步骤(借助 Action)
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
command: ./flaky-test.sh
常用 Actions
代码检出与工具安装
steps:
# 检出代码
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史(SonarQube 需要)
ref: ${{ github.head_ref }} # PR 时检出源分支
# Java
- uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin" # temurin / corretto / zulu / microsoft
cache: "maven" # 自动缓存 Maven 依赖
# Node.js
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm" # 自动缓存 npm
# Python
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
# Go
- uses: actions/setup-go@v5
with:
go-version: "1.22"
cache: true
缓存
- uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.gradle/caches
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
产物上传/下载
# 上传
- uses: actions/upload-artifact@v4
with:
name: build-output
path: |
target/*.jar
dist/
retention-days: 7
if-no-files-found: error # error | warn | ignore
# 下载(在另一个 Job 中)
- uses: actions/download-artifact@v4
with:
name: build-output
path: ./artifacts
Docker 构建与推送
- name: Set up QEMU(多平台构建)
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=sha,prefix=sha-
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha # 使用 GitHub Actions 缓存层
cache-to: type=gha,mode=max
GitHub Release
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: Release ${{ github.ref_name }}
body_path: CHANGELOG.md
files: |
dist/*.zip
target/*.jar
draft: false
prerelease: ${{ contains(github.ref_name, '-rc') }}
代码质量
# SonarCloud
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# CodeQL(GitHub 原生代码扫描)
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: java, javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Secrets 与变量管理
分级管理
| 级别 | 路径 | 作用域 |
|---|---|---|
| Repository Secret | Settings → Secrets → Actions | 当前仓库 |
| Repository Variable | Settings → Variables → Actions | 当前仓库 |
| Environment Secret | Settings → Environments → 指定环境 | 特定环境的 Job |
| Organization Secret | Org Settings → Secrets | 组织内多个仓库 |
env:
# 引用 Secret(加密,日志自动打码)
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
# 引用 Variable(明文)
API_URL: ${{ vars.API_URL }}
# GitHub 自动提供的 Token(当前仓库权限)
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN 权限
# 工作流级别权限(最小权限原则)
permissions:
contents: read
packages: write # 推送到 GHCR 需要
pull-requests: write # 评论 PR 需要
id-token: write # OIDC 认证需要
# Job 级别覆盖
jobs:
deploy:
permissions:
id-token: write
contents: read
OIDC 无密钥认证(推荐,代替长期凭据)
# 以 AWS 为例,无需存储 Access Key
jobs:
deploy:
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-role
aws-region: cn-north-1
- run: aws s3 sync dist/ s3://my-bucket/
可复用工作流(Reusable Workflows)
定义可复用工作流
# .github/workflows/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
env:
TOKEN: ${{ secrets.deploy-token }}
run: |
echo "部署 ${{ inputs.image-tag }} 到 ${{ inputs.environment }}"
./deploy.sh ${{ inputs.environment }} ${{ inputs.image-tag }}
调用可复用工作流
# .github/workflows/ci.yml
jobs:
build:
uses: ./.github/workflows/build.yml # 本仓库
deploy-staging:
needs: build
uses: my-org/shared-workflows/.github/workflows/deploy.yml@main # 其他仓库
with:
environment: staging
image-tag: ${{ needs.build.outputs.image-tag }}
secrets:
deploy-token: ${{ secrets.STAGING_DEPLOY_TOKEN }}
deploy-prod:
needs: deploy-staging
uses: my-org/shared-workflows/.github/workflows/deploy.yml@main
with:
environment: production
image-tag: ${{ needs.build.outputs.image-tag }}
secrets: inherit # 继承调用方所有 secrets
Composite Action(自定义复合 Action)
将多个步骤封装为一个 Action,在多个工作流中复用。
# .github/actions/setup-and-cache/action.yml
name: "Setup Node and Cache"
description: "安装 Node.js 并恢复缓存"
inputs:
node-version:
description: "Node.js 版本"
required: false
default: "20"
outputs:
cache-hit:
description: "是否命中缓存"
value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: "composite"
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- id: cache
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- if: steps.cache.outputs.cache-hit != 'true'
run: npm ci
shell: bash
# 在工作流中使用
steps:
- uses: ./.github/actions/setup-and-cache
with:
node-version: "20"
Self-hosted Runner
安装 Runner
# 在 GitHub 仓库:Settings → Actions → Runners → New self-hosted runner
# 按页面上的命令操作,以 Linux 为例:
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.x.x.tar.gz -L https://github.com/actions/runner/releases/download/v2.x.x/actions-runner-linux-x64-2.x.x.tar.gz
tar xzf ./actions-runner-linux-x64-2.x.x.tar.gz
# 配置(按页面上的 token)
./config.sh --url https://github.com/your-org/your-repo --token YOUR_TOKEN --labels "self-hosted,linux,docker"
# 安装为服务
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status
使用 Self-hosted Runner
jobs:
build:
runs-on: self-hosted # 使用任意 self-hosted runner
deploy:
runs-on: [self-hosted, linux, docker] # 匹配标签
Docker 中运行 Runner(推荐)
# docker-compose.yml
version: "3.8"
services:
runner:
image: myoung34/github-runner:latest
restart: unless-stopped
environment:
RUNNER_SCOPE: repo
REPO_URL: https://github.com/your-org/your-repo
RUNNER_TOKEN: YOUR_TOKEN
RUNNER_NAME: docker-runner-1
LABELS: docker,linux
RUNNER_WORKDIR: /tmp/runner/work
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /tmp/runner:/tmp/runner
完整示例
Java Spring Boot CI/CD
name: Java CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
JAVA_VERSION: "17"
jobs:
# ── Build & Test ──────────────────────────────────
test:
name: Test
runs-on: ubuntu-latest
permissions:
contents: read
checks: write # 发布测试报告
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: temurin
cache: maven
- name: Run Tests
run: mvn verify
- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: "**/target/surefire-reports/*.xml"
- name: Upload Coverage
uses: codecov/codecov-action@v4
with:
files: target/site/jacoco/jacoco.xml
token: ${{ secrets.CODECOV_TOKEN }}
# ── Build Docker Image ────────────────────────────
build-image:
name: Build Image
needs: test
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: temurin
cache: maven
- name: Build JAR
run: mvn package -DskipTests
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha,prefix=sha-,format=short
- 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
# ── Deploy to Dev ─────────────────────────────────
deploy-dev:
name: Deploy Dev
needs: build-image
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment:
name: development
url: https://dev.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to Dev
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEV_HOST }}
username: ${{ secrets.DEV_USER }}
key: ${{ secrets.DEV_SSH_KEY }}
script: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build-image.outputs.image-tag }}
docker stop app || true
docker rm app || true
docker run -d --name app --restart unless-stopped \
-p 8080:8080 \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build-image.outputs.image-tag }}
# ── Deploy to Production ──────────────────────────
deploy-prod:
name: Deploy Production
needs: build-image
runs-on: ubuntu-latest
if: github.event_name == 'release'
environment:
name: production
url: https://app.example.com
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: cn-north-1
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster prod-cluster \
--service app-service \
--force-new-deployment
- name: Wait for stable
run: |
aws ecs wait services-stable \
--cluster prod-cluster \
--services app-service
# ── Notify ────────────────────────────────────────
notify:
name: Notify
needs: [deploy-dev, deploy-prod]
runs-on: ubuntu-latest
if: always()
steps:
- name: Send notification
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "${{ needs.deploy-prod.result == 'success' && '✅' || '❌' }} ${{ github.repository }} - ${{ github.workflow }} ${{ needs.deploy-prod.result }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Node.js 前端 CI/CD
name: Frontend CI/CD
on:
push:
branches: [main]
paths: ["src/**", "package*.json", "vite.config.*"]
pull_request:
branches: [main]
jobs:
quality:
name: Lint & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Unit tests
run: npm run test:unit -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
retention-days: 3
build:
name: Build
needs: quality
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
strategy:
matrix:
env: [staging, production]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- name: Build (${{ matrix.env }})
run: npm run build
env:
VITE_APP_ENV: ${{ matrix.env }}
VITE_API_URL: ${{ vars[format('API_URL_{0}', upper(matrix.env))] }}
- uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.env }}
path: dist/
retention-days: 3
deploy:
name: Deploy to ${{ matrix.env }}
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
include:
- env: staging
bucket: s3://staging-bucket
distribution: STAGING_CF_ID
- env: production
bucket: s3://prod-bucket
distribution: PROD_CF_ID
environment:
name: ${{ matrix.env }}
url: ${{ matrix.env == 'production' && 'https://app.example.com' || 'https://staging.example.com' }}
steps:
- uses: actions/download-artifact@v4
with:
name: dist-${{ matrix.env }}
path: dist/
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets[format('AWS_ROLE_{0}', upper(matrix.env))] }}
aws-region: cn-north-1
- name: Deploy to S3
run: aws s3 sync dist/ ${{ matrix.bucket }} --delete
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets[matrix.distribution] }} \
--paths "/*"
常用工作流片段
PR 自动标签
name: PR Labeler
on:
pull_request:
types: [opened, synchronize]
jobs:
label:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
# .github/labeler.yml
frontend:
- changed-files:
- any-glob-to-any-file: ["src/frontend/**", "*.css", "*.scss"]
backend:
- changed-files:
- any-glob-to-any-file: ["src/backend/**", "pom.xml"]
自动合并 Dependabot PR
name: Auto merge Dependabot
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- uses: dependabot/fetch-metadata@v2
id: metadata
- name: Auto merge patch and minor updates
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
代码检查(PR 评论)
name: Code Review Bot
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run ESLint and comment
uses: reviewdog/action-eslint@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review # 在 PR 行级别发表评论
eslint_flags: "src/"
最佳实践
-
锁定 Action 版本:使用 commit SHA 而非 tag(如
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683),防止供应链攻击。版本 tag 可能被覆盖,SHA 不可变。 -
最小 GITHUB_TOKEN 权限:工作流顶层设置
permissions: read-all,各 Job 按需授权,遵循最小权限原则。 -
不在工作流中硬编码密钥:所有敏感信息通过
${{ secrets.XXX }}引用,优先使用 OIDC 代替长期凭据。 -
缓存加速:使用
actions/cache或 setup-* Action 内置缓存(Maven、npm、pip),缓存 key 以锁文件哈希为准。 -
并发控制:生产部署配置
concurrency防止并发部署竞争,PR 场景设cancel-in-progress: true节省资源。 -
PR 快速反馈:lint、单测等轻量检查在 PR 阶段运行,完整构建和部署只在 push main/release 时触发。
-
矩阵测试:用
matrix覆盖多平台 / 多版本,fail-fast: false确保不因一个组合失败而取消所有。 -
可复用工作流:公共的 build / deploy 逻辑抽取为 Reusable Workflow 或 Composite Action,在组织内共享。
-
Environment 保护:生产环境配置 Required Reviewers,确保人工审批后才部署,同时限制只有特定分支可以访问。
-
构建产物版本化:使用
docker/metadata-action自动生成语义化镜像 Tag,保证可追溯和可回滚。