> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# GitHub Actions 完全指南
- URL: https://blog.vercanti.com/github-actions-wan-quan-zhi-nan/
- Published: 2026-08-28T14:34:24.000Z
- Updated: 2026-08-28T14:56:26.000Z
- Description: 相关文档：Docker Compose完全指南(/docker-compose-wan-quan-zhi-nan/) Docker高级指南(/docker-gao-ji-zhi-nan/) pytest完全指南(/pytest-wan-quan-zhi-nan/) GitHub Actions 是 GitHub 内置的 CI/CD 平台，通过在仓库的 .github/workflows/ 目录下创建 YAML 文件定义自动化流程。 GitHub Actions 对 YAML 缩进敏感，uses 和 run 必须在同一级： 在 Windows Runner
- Author: yellowdog
- Tags: DevOps

> 官方文档：<https://docs.github.com/zh/actions>  
> 适用版本：GitHub Actions（2026-05-07 核实）

相关文档：[Docker Compose完全指南](https://blog.vercanti.com/docker-compose-wan-quan-zhi-nan/) [Docker高级指南](https://blog.vercanti.com/docker-gao-ji-zhi-nan/) [pytest完全指南](https://blog.vercanti.com/pytest-wan-quan-zhi-nan/)

---

## 1\. 基础概念

### GitHub Actions 是什么

GitHub Actions 是 GitHub 内置的 CI/CD 平台，通过在仓库的 `.github/workflows/` 目录下创建 YAML 文件定义自动化流程。

| 概念            | 说明                              |
| ------------- | ------------------------------- |
| Workflow（工作流） | 一个 YAML 文件，定义一套完整的自动化流程         |
| Event（触发事件）   | 触发 Workflow 的事件（push、PR、cron 等） |
| Job（作业）       | Workflow 中的一个独立任务单元，默认并行运行      |
| Step（步骤）      | Job 中的一个操作，按顺序执行                |
| Action（动作）    | 可复用的步骤单元（marketplace 有大量现成的）    |
| Runner（运行器）   | 执行 Job 的服务器（GitHub 托管或自托管）      |

### 免费额度（GitHub 托管 Runner）

| 账户类型   | 免费分钟数 / 月      |
| ------ | -------------- |
| 个人免费账户 | 2000 分钟（Linux） |
| 公开仓库   | 无限制            |

---

## 2\. Workflow 文件结构

```yaml
# .github/workflows/ci.yml
name: CI                   # Workflow 名称

on:                         # 触发条件
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:                     # Job ID
    name: Run Tests         # Job 显示名
    runs-on: ubuntu-latest  # 运行环境

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run tests
        run: pytest

```

---

## 3\. 触发事件（on）

```yaml
on:
  # 推送到指定分支
  push:
    branches:
      - main
      - "release/**"     # 通配符
    paths:
      - "src/**"         # 只有 src/ 下的文件变化才触发
    paths-ignore:
      - "docs/**"
      - "*.md"

  # Pull Request
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [main]

  # 定时任务（cron）
  schedule:
    - cron: "0 2 * * *"    # 每天凌晨 2 点 UTC

  # 手动触发
  workflow_dispatch:
    inputs:
      environment:
        description: "部署环境"
        required: true
        default: "staging"
        type: choice
        options: [staging, production]

  # 其他 Workflow 调用
  workflow_call:

  # 发布 Release
  release:
    types: [published]

```

---

## 4\. Job 配置

```yaml
jobs:
  build:
    runs-on: ubuntu-latest   # ubuntu-latest / ubuntu-22.04 / windows-latest / macos-latest

    # 并发控制：同一 PR 只保留最新的 Workflow 运行
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
      cancel-in-progress: true

    # 超时限制
    timeout-minutes: 30

    # 环境变量（Job 级别）
    env:
      PYTHONPATH: src
      DATABASE_URL: sqlite:///test.db

    # 矩阵策略：多版本并行测试
    strategy:
      fail-fast: false  # 一个失败不取消其他
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
        os: [ubuntu-latest, macos-latest]

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

```

---

## 5\. 常用步骤

### Checkout 代码

```yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # 获取完整历史（覆盖率比较需要）

```

### 设置 Python 环境

```yaml
- uses: actions/setup-python@v5
  with:
    python-version: "3.12"
    cache: "pip"  # 缓存 pip 依赖

- name: Install dependencies
  run: |
    pip install -r requirements.txt
    pip install -r requirements-dev.txt

```

### 缓存依赖

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

```

### 设置 Node.js 环境

```yaml
- uses: actions/setup-node@v4
  with:
    node-version: "20"
    cache: "npm"

- run: npm ci

```

---

## 6\. 实用 Workflow 模板

### Python CI（测试 + 代码质量）

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

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: testuser
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: pip

      - name: Install dependencies
        run: pip install -r requirements.txt -r requirements-dev.txt

      - name: Lint（Ruff）
        run: ruff check src tests

      - name: Type check（mypy）
        run: mypy src

      - name: Run tests with coverage
        env:
          DATABASE_URL: postgresql+asyncpg://testuser:testpass@localhost:5432/testdb
        run: pytest --cov=src --cov-report=xml -v

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: coverage.xml

```

### 前端 CI（Lint + 构建 + 测试）

```yaml
# .github/workflows/frontend-ci.yml
name: Frontend CI

on:
  push:
    branches: [main]
    paths: ["frontend/**"]
  pull_request:
    paths: ["frontend/**"]

defaults:
  run:
    working-directory: frontend

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
          cache-dependency-path: frontend/package-lock.json

      - run: npm ci

      - name: Type check
        run: npm run type-check

      - name: Lint
        run: npm run lint

      - name: Build
        run: npm run build

      - name: Test
        run: npm run test

```

### Docker 构建 + 推送

```yaml
# .github/workflows/docker-publish.yml
name: Docker Publish

on:
  release:
    types: [published]
  push:
    branches: [main]

jobs:
  build-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      # 设置 Docker Buildx（多平台构建）
      - uses: docker/setup-buildx-action@v3

      # 登录 GitHub Container Registry
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # 生成标签（main 分支 → latest，release → 版本号）
      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=sha-

      # 构建并推送
      - uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha    # 使用 GitHub Actions 缓存加速构建
          cache-to: type=gha,mode=max

```

### 部署到服务器（SSH）

```yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # 关联 GitHub 环境，可配置审批

    steps:
      - uses: actions/checkout@v4

      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/myapp
            git pull origin main
            docker compose pull
            docker compose up -d --no-deps web
            docker compose exec web alembic upgrade head

```

---

## 7\. Secrets 与变量

```yaml
# 使用 Secrets（Settings → Secrets and variables → Actions）
steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.API_KEY }}
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
    run: ./deploy.sh

# 使用 Variables（非敏感配置）
  - name: Build
    env:
      APP_VERSION: ${{ vars.APP_VERSION }}
      ENVIRONMENT: ${{ vars.ENVIRONMENT }}
    run: npm run build

```

### 内置变量

| 变量                 | 说明                          |
| ------------------ | --------------------------- |
| github.sha         | 触发 Workflow 的 commit SHA    |
| github.ref         | 触发的分支/标签（refs/heads/main）   |
| github.ref\_name   | 分支/标签名（main）                |
| github.actor       | 触发者用户名                      |
| github.event\_name | 事件类型（push/pull\_request）    |
| github.repository  | 仓库名（owner/repo）             |
| github.run\_id     | 本次运行 ID                     |
| runner.os          | 运行器 OS（Linux/Windows/macOS） |

---

## 8\. Job 间数据传递

### 输出变量

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.get_version.outputs.version }}

    steps:
      - id: get_version
        run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying version ${{ needs.build.outputs.version }}"

```

### 上传/下载 Artifact

```yaml
# 上传构建产物
- uses: actions/upload-artifact@v4
  with:
    name: dist
    path: dist/
    retention-days: 7

# 在另一个 job 下载
- uses: actions/download-artifact@v4
  with:
    name: dist
    path: dist/

```

---

## 9\. 最佳实践

### 固定 Action 版本，使用 SHA 而非 tag（安全）

```yaml
# 不推荐（tag 可以被强制推送覆盖）
uses: actions/checkout@v4

# 推荐（SHA 不可变，防止供应链攻击）
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

```

### 最小权限原则

```yaml
permissions:
  contents: read     # 只读代码
  packages: write    # 写 GitHub Packages（只有需要时才开）

```

### 耗时任务加缓存

```yaml
# pip、npm、cargo、maven 依赖都应该缓存
- uses: actions/setup-python@v5
  with:
    python-version: "3.12"
    cache: pip  # 自动缓存

```

---

## 10\. 踩坑与注意事项

### YAML 缩进问题

GitHub Actions 对 YAML 缩进敏感，`uses` 和 `run` 必须在同一级：

```yaml
# 错误
steps:
  - name: test
  uses: actions/checkout@v4  # 缩进错误！

# 正确
steps:
  - name: test
    uses: actions/checkout@v4

```

### services 容器需要时间启动

```yaml
# PostgreSQL 需要健康检查，不能直接用
services:
  postgres:
    image: postgres:16
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-retries 5

```

### Windows Runner 换行符问题

在 Windows Runner 上，shell 脚本的换行符可能导致问题，明确指定 shell：

```yaml
- name: Run script
  shell: bash   # 强制使用 bash（Windows 上也有）
  run: echo "hello"

```

---

## 最佳实践

**将机密存入 Repository Secrets，不硬编码**：API 密钥、Docker 凭证等敏感信息必须存入 GitHub Secrets（Settings → Secrets and variables → Actions），通过 `${{ secrets.MY_KEY }}` 引用，不出现在 YAML 或日志中。

**使用 `cache` action 加速依赖安装**：Python pip、Node npm、Go mod 每次都下载会导致 CI 耗时 3–5 分钟，缓存后通常 15–30 秒恢复。

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: pip-${{ hashFiles('requirements.txt') }}
    restore-keys: pip-

```

**用 matrix 策略做多版本测试**：一次定义，自动在多个 Python/Node 版本上并行运行，比重复写多个 job 简洁。

```yaml
strategy:
  matrix:
    python-version: ["3.11", "3.12", "3.13"]
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python-version }}

```

**部署步骤加 `environment` 保护**：对 production 部署的 job 设置 `environment: production`，可以配置审批者（required reviewers），防止误推。

**用 `concurrency` 避免同一分支并行部署**：PR 多次推送会触发多个 Workflow 同时运行，配置 `concurrency` 自动取消旧的运行。

```yaml
concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true

```

---

## 常见陷阱

### 陷阱：secrets 在 fork 的 PR 中不可用

**现象：** 外部贡献者提交 PR 后，CI 中访问 `secrets.DOCKER_TOKEN` 报错或为空。

**原因：** 出于安全考虑，fork 仓库触发的 pull\_request 事件无法访问父仓库的 secrets，防止恶意 PR 窃取密钥。

**解决：** 使用 `pull_request_target` 事件（谨慎使用，有安全风险）或将需要 secrets 的步骤移到需要 merge 后才触发的工作流（`push` 到 main）。

---

### 陷阱：job 间共享文件需要 artifact

**现象：** Job A 生成了一个构建产物，Job B 需要使用，但 Job B 找不到该文件。

**原因：** 每个 job 在独立的 runner 实例上运行，文件系统不共享。

**解决：** Job A 用 `upload-artifact` 上传，Job B 用 `download-artifact` 下载。

```yaml
# Job A
- uses: actions/upload-artifact@v4
  with:
    name: dist
    path: dist/

# Job B（需声明 needs: [build]）
- uses: actions/download-artifact@v4
  with:
    name: dist

```

---

### 陷阱：workflow 文件语法错误静默失败

**现象：** 推送后 workflow 没有触发，或 GitHub 显示 workflow 文件错误。

**原因：** YAML 缩进错误（用 tab 而非空格）、`on` 关键字不加引号被解析为布尔值、step 的 `uses` 和 `run` 同时存在等常见语法错误。

**解决：** 用 `actionlint` 本地静态检查 workflow 文件；在 VS Code 中安装 GitHub Actions 扩展实时提示。

```bash
# 本地检查
brew install actionlint
actionlint .github/workflows/*.yml

```

---

## 参见

- [Docker高级指南](https://blog.vercanti.com/docker-gao-ji-zhi-nan/)
- [Docker Compose完全指南](https://blog.vercanti.com/docker-compose-wan-quan-zhi-nan/)
- [pytest完全指南](https://blog.vercanti.com/pytest-wan-quan-zhi-nan/)
- [Git进阶指南](https://blog.vercanti.com/git-jin-jie-zhi-nan/)