返回文章列表

文章

Github Actions使用指南

从基础概念、配置结构、常用用例与最佳实践四个方面,系统介绍如何使用 GitHub Actions 来自动化软件开发流程

目录
  1. 以下是一个完整的 GitHub Actions 使用指南,将基础概念、工作流结构、常见用例与最佳实践,帮助你在实际项目中更高效地编写和管理 CI/CD 流程。
  2. 一、基础概念与术语
  3. 4. Context 与 Expression(上下文与表达式) - 在 Workflow 中,用 ${{ ... }} 来引用上下文变量(Context)并做简单逻辑判断。 - 常用上下文示例: - github.ref、github.ref_name:当前 Branch 或 Tag 名称; - github.actor:发起事件的用户名; - runner.os:Runner 操作系统; - secrets.MY_SECRET:访问仓库中 Secrets 中的机密。 - Expression 例子: yaml if: ${{ github.ref == 'refs/heads/main' }} 或者在 Matrix 中: yaml runs-on: ${{ matrix.os }} 5. Job(作业) - Workflow 里并行或串行执行的一组 Steps。 - 默认不同 Job 之间彼此隔离、在不同 Runner 上运行,无法共享文件系统。 - 可以用 needs: 来声明 Job 之间的依赖,保证有依赖关系的 Job 按顺序执行。 6. Step(步骤) - Job 中的最小执行单元。可以是: - uses: 某个 Action——复用社区或官方 Action; - run: 某段 Shell/PowerShell 命令——由 Runner 直接执行。 - 可以通过 name: 给 Step 命名,在日志中更易识别。 7. Action(动作) - 单个独立的可复用组件,封装了某种任务(如检出代码、安装依赖、上传 Release 等)。 - 官方和社区发布了大量 Action,可直接在 .github/workflows/xxx.yml 中引用,例如: yaml uses: actions/checkout@v3 uses: actions/cache@v3 uses: actions/setup-node@v3 - 也可以自行编写并发布 Action(JavaScript、Docker、Composite Action 均可)。
  4. 二、工作流的基本结构
  5. 结构说明
  6. 三、常见配置及进阶用法
  7. 1. 触发器中的包含/排除路径(paths 与 paths-ignore)
  8. 1.1 paths(包含规则)
  9. 1.2 paths-ignore(排除规则)
  10. 1.3 同时使用 paths 和 paths-ignore
  11. 2. 缓存依赖(Cache)
  12. 3. 矩阵构建(Matrix)
  13. ① matrix.os 会分别取 ubuntu-latest、windows-latest、macos-latest, ② matrix.rust-version 会分别取 1.65.0、1.66.0, 这样一共会启动 3×2=6 个并行 Job,覆盖多环境多版本测试。
  14. 4. 多 Job 串行与并行
  15. 5. 使用 Secrets 与环境变量
  16. 6. 手动触发与输入参数(workflow_dispatch)
  17. 7. 定时触发(schedule)
  18. 四、示例场景
  19. 1. Node.js + Docker 自动化构建与发布
  20. 2. Rust 多平台构建与发布(Matrix + paths-ignore)
  21. 五、调试与最佳实践
  22. release: if: ${{ startsWith(github.ref, 'refs/tags/') }} # 只在 Tag 推送时跑发布 ```
  23. 六、小结
  24. 📎 参考文章

以下是一个完整的 GitHub Actions 使用指南,将基础概念、工作流结构、常见用例与最佳实践,帮助你在实际项目中更高效地编写和管理 CI/CD 流程。#

一、基础概念与术语#

  1. Runner(运行器)
    • 负责在特定环境(如 Ubuntu、Windows、macOS)下执行你定义的任务。
    • GitHub 提供托管型 Runner(如 ubuntu-latestwindows-latestmacos-latest),也支持自托管 Runner。
  2. Workflow(工作流)
    • 存放在仓库的 .github/workflows/ 目录下,以 YAML 格式描述。
    • 定义触发条件(Trigger)、一个或多个 Job,以及每个 Job 中的执行步骤(Step)。
    • 每当满足触发条件时,GitHub Actions 会启动对应的 Workflow。
  3. Trigger(触发器)
    • 规定何时启动 Workflow。常见写法在 YAML 顶层的 on: 字段下声明。
    • 例如:

on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: '0 3 * * *' workflow_dispatch: {}

	```
- **`push`**** / ****`pull_request`**:代码推送或 PR 创建/更新时触发;
- **`schedule`**:定时触发,例如每天凌晨;
- **`workflow_dispatch`**:手动触发,可以在仓库 UI 上点击按钮并传入自定义参数。

4. Context 与 Expression(上下文与表达式) - 在 Workflow 中,用 ${{ ... }} 来引用上下文变量(Context)并做简单逻辑判断。 - 常用上下文示例: - github.refgithub.ref_name:当前 Branch 或 Tag 名称; - github.actor:发起事件的用户名; - runner.os:Runner 操作系统; - secrets.MY_SECRET:访问仓库中 Secrets 中的机密。 - Expression 例子: yaml if: ${{ github.ref == 'refs/heads/main' }} 或者在 Matrix 中: yaml runs-on: ${{ matrix.os }} 5. Job(作业) - Workflow 里并行或串行执行的一组 Steps。 - 默认不同 Job 之间彼此隔离、在不同 Runner 上运行,无法共享文件系统。 - 可以用 needs: 来声明 Job 之间的依赖,保证有依赖关系的 Job 按顺序执行。 6. Step(步骤) - Job 中的最小执行单元。可以是: - uses: 某个 Action——复用社区或官方 Action; - run: 某段 Shell/PowerShell 命令——由 Runner 直接执行。 - 可以通过 name: 给 Step 命名,在日志中更易识别。 7. Action(动作) - 单个独立的可复用组件,封装了某种任务(如检出代码、安装依赖、上传 Release 等)。 - 官方和社区发布了大量 Action,可直接在 .github/workflows/xxx.yml 中引用,例如: yaml uses: actions/checkout@v3 uses: actions/cache@v3 uses: actions/setup-node@v3 - 也可以自行编写并发布 Action(JavaScript、Docker、Composite Action 均可)。#

二、工作流的基本结构#

下面举一个最基础的示例,并逐层拆解其含义。假设文件路径为 .github/workflows/ci.yml

name: CI Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build_and_test:
    name: Build & Test
    runs-on: ubuntu-latest

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

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm test

结构说明#

  1. name: CI Pipeline Workflow 的可读名称,会显示在 GitHub Actions 页面顶部。
  2. on: 定义了什么时候触发这个 Workflow:
    • push.branches: [ main ]:当向 main 分支推送时执行。
    • pull_request.branches: [ main ]:当向 main 分支发起或更新 PR 时也会执行。
  3. jobs: 下面可以定义多个 Job。本示例只有一个 Job,名字叫 build_and_test
    • runs-on: ubuntu-latest:指定 Runner 类型为最新的 Ubuntu 虚拟机。
  4. steps:
    • uses: actions/checkout@v3:检出仓库到 Runner。
    • uses: actions/setup-node@v3:安装并配置 Node.js 环境。
    • run: npm install:运行 Shell 命令安装依赖。
    • run: npm test:运行 Shell 命令执行测试。

三、常见配置及进阶用法#

1. 触发器中的包含/排除路径(pathspaths-ignore#

1.1 paths(包含规则)#

on:
  push:
    branches:
      - main
    paths:
      - 'src/**'
      - 'package.json'

  • 只有当推送的改动中至少有一个文件匹配 src/**package.json 时,才会触发 Workflow。
  • 如果改动中没有任何文件落在这些路径下,Workflow 直接跳过。

1.2 paths-ignore(排除规则)#

on:
  push:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
      - 'README.md'
      - '.github/**'

  • 如果此次推送中所有改动的文件都匹配 docs/**README.md.github/**,则不触发 Workflow。
  • 只有当改动中存在至少一个文件不在忽略列表里时,Workflow 才会触发。
  • 典型场景
    • 文档 (docs/) 单独更新时不跑 CI;
    • .github/workflows/ 下的 YAML 或 README 做改动时跳过。

1.3 同时使用 pathspaths-ignore#

on:
  push:
    branches:
      - main
    paths:
      - 'src/**'
    paths-ignore:
      - 'src/legacy/**'
      - 'README.md'

  • 先看“是否有改动在 paths 列表里”——如果没有,就不触发;
  • 如果有,再看“这些改动是否都在 paths-ignore 中”——如果都在忽略列表,就不触发;否则触发。

2. 缓存依赖(Cache)#

CI 中反复安装依赖会耗费大量时间。通过缓存可以显著加速:

  • Node.js 示例

  • name: Cache node_modules uses: actions/cache@v3 with: path: node_modules key: runner.osnode{{ runner.os }}-node-runner.osnode{{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-

  • Rust 示例

  • name: Cache Cargo registry and build artifacts uses: actions/cache@v3 with: path: | ~/.cargo/registry ~/.cargo/git target key: runner.oscargo{{ runner.os }}-cargo-runner.oscargo{{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-

解释:

  1. path:要缓存的目录,写多个路径时用列表形式。
  2. key:缓存的唯一标识,通常使用依赖锁文件(如 package-lock.jsonCargo.lock)的哈希,以确保依赖变化时重新缓存。
  3. restore-keys:如果没有完全命中 key,会尝试前缀匹配,使用更老的缓存。

3. 矩阵构建(Matrix)#

当想在多种环境(操作系统、语言版本、数据库版本等)下并行运行相同任务时,可使用矩阵:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        rust-version: [1.65.0, 1.66.0]

    steps:
      - uses: actions/checkout@v3

      - name: Setup Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ matrix.rust-version }}
          override: true

      - name: Run tests
        run: cargo test --verbose

matrix.os 会分别取 ubuntu-latestwindows-latestmacos-latest, ② matrix.rust-version 会分别取 1.65.01.66.0, 这样一共会启动 3×2=6 个并行 Job,覆盖多环境多版本测试。#

4. 多 Job 串行与并行#

默认情况下,一个 Workflow 中的所有 Job 并行运行。如果需要某个 Job 在另一个完成后再跑,则可用 needs:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: cargo build --release

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v3
      - run: ./deploy.sh
  • 这样只有当 build 成功后,才会触发 deploy
  • build 失败,deploy 将被跳过。

5. 使用 Secrets 与环境变量#

当需要提供访问令牌、私钥等敏感信息时,应将它们存放在仓库的 Settings → Secrets 中,然后在 Workflow 中以 ${{ secrets.NAME }} 的方式引用。例如:

env:
  MY_SECRET: ${{ secrets.MY_SECRET }}

steps:
  - name: Use Secret
    run: |
      echo "The secret is $MY_SECRET"
  • GitHub Actions 会自动对日志中出现的 secrets 值进行遮蔽(**),但尽量避免在 run 里明文打印完整内容。

6. 手动触发与输入参数(workflow_dispatch#

如果希望在 GitHub UI 里手动触发 Workflow,并提供自定义参数,可这样写:

on:
  workflow_dispatch:
    inputs:
      environment:
        description: '部署环境'
        required: true
        default: 'staging'
  • 触发时,用户可以在页面中填写 environment,例如 productionstaging
  • 在后续 Steps 中可用 ${{ github.event.inputs.environment }} 来获取该值。

7. 定时触发(schedule#

on:
  schedule:
    - cron: '0 3 * * *'
  • GitHub Actions 使用 UTC 时间。如果想在北京时间凌晨 3 点运行,需要把 cron 写成 UTC 对应的 19 * * *
  • 定时任务通常用来执行:自动化备份、定期扫描、安全检查、生成报告等。

四、示例场景#

下面以几个常见语言/框架为例,演示完整的 CI/CD 工作流。

1. Node.js + Docker 自动化构建与发布#

name: Node.js CI & Docker Build

on:
  push:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
      - '.github/**'
  pull_request:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
      - '.github/**'
  workflow_dispatch: {}

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js 18
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Cache npm dependencies
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install deps
        run: npm ci

      - name: Lint code
        run: npm run lint

      - name: Run tests
        run: npm test

  build-and-push-docker:
    name: Build & Push Docker Image
    runs-on: ubuntu-latest
    needs: test
    if: ${{ github.ref == 'refs/heads/main' }}

    steps:
      - uses: actions/checkout@v3

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: myrepo/myapp:latest

  • 触发条件
    • pushmain,且改动不全在 docs/.github/
    • pull_request 针对 main,同样忽略文档改动;
    • 支持手动触发。
  • Job “test”:检出 → 安装 Node.js → 缓存依赖 → 安装 → Lint → 测试。
  • Job “build-and-push-docker”:依赖于测试成功,仅当分支是 main 时执行,构建并推送 Docker 镜像。

2. Rust 多平台构建与发布(Matrix + paths-ignore)#

name: Rust CI & Release

on:
  push:
    branches:
      - main
    tags:
      - 'v*'
    paths-ignore:
      - 'docs/**'
      - '.github/**'
  pull_request:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
      - '.github/**'

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    name: Build & Release (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    if: ${{ startsWith(github.ref, 'refs/tags/') }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            cache_registry: ~/.cargo/registry
            cache_git:     ~/.cargo/git
            shell: bash
            ext: tar.gz
            target: linux-x86_64
          - os: windows-latest
            cache_registry: "C:\\Users\\runneradmin\\.cargo\\registry"
            cache_git:      "C:\\Users\\runneradmin\\.cargo\\git"
            shell: powershell
            ext: zip
            target: windows-x86_64
          - os: macos-latest
            cache_registry: ~/.cargo/registry
            cache_git:     ~/.cargo/git
            shell: bash
            ext: tar.gz
            target: macos-x86_64

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup Rust toolchain
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          profile: minimal
          override: true

      - name: Cache Cargo registry & build artifacts
        uses: actions/cache@v3
        with:
          path: |
            ${{ matrix.cache_registry }}
            ${{ matrix.cache_git }}
            target
          key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ matrix.os }}-cargo-

      - name: Install sqlx-cli
        run: cargo install sqlx-cli --no-default-features --features postgres
        shell: ${{ matrix.shell }}

      - name: Create DB & Run migrations
        run: |
          sqlx database create || echo "database already exists"
          sqlx migrate run
        shell: ${{ matrix.shell }}

      - name: Run tests
        run: cargo test --verbose
        shell: ${{ matrix.shell }}

      - name: Build (release)
        run: cargo build --release
        shell: ${{ matrix.shell }}

      - name: Package (Windows)
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
          New-Item -ItemType Directory -Force -Path dist
          Copy-Item target\release\zero2prod.exe dist\
          Compress-Archive -Path dist\zero2prod.exe -DestinationPath zero2prod-${{ github.ref_name }}-${{ matrix.target }}.${{ matrix.ext }}
        shell: powershell

      - name: Package (Unix)
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
          mkdir -p dist
          cp target/release/zero2prod dist/
          chmod +x dist/zero2prod
          tar -czf zero2prod-${{ github.ref_name }}-${{ matrix.target }}.${{ matrix.ext }} -C dist zero2prod
        shell: bash

      - name: Upload Artifact
        uses: softprops/action-gh-release@v1
        with:
          files: zero2prod-${{ github.ref_name }}-${{ matrix.target }}.${{ matrix.ext }}

  • 触发条件
    • pushmain 分支或形如 v* 的 Tag 推送时。
    • 只要改动全部属于 docs/.github/,即不触发。
  • 矩阵构建内容
    • 三个平台:ubuntu-latestwindows-latestmacos-latest
    • 对应不同的缓存路径(Linux/Unix vs. Windows)、不同的 Shell;
    • 全平台统一安装 Rust → 缓存依赖 → 安装 sqlx-cli → 数据库迁移 → 测试 → Release 构建 → 平台打包 → 上传到 GitHub Release。
  • 注意
    • Windows/macOS 上官方不支持 services: 来启动容器化数据库,所以如果需要跑集成测试,建议仅在 Linux Job 中启动 Postgres(本示例中为了完整性而仍写了相关步骤,实际可根据项目需要删减);
    • 如果只想在 Linux 上做集成测试,可在 Windows/macOS 部分去掉数据库相关步骤,只保留构建/打包发布。

五、调试与最佳实践#

  1. 日志与调试
    • 当某个 Step 或 Job 失败,浏览器中会显示红色❌,点击展开可以看到详细日志。
    • 如果脚本输出不足,可以在 Step 里加上 set -x(Bash)或者 Write-Host(PowerShell)来打印更多调试信息。
  2. 避免泄露敏感信息
    • 使用 ${{ secrets.MY_SECRET }} 引用 Secrets,不要在日志中明文打印。
    • GitHub 会自动遮蔽 secrets 的输出,但务必谨慎对待。
  3. 善用缓存(Cache)
    • 依赖缓存可以大幅加速 CI,推荐对于大多数语言/包管理工具都做相应缓存。
    • 缓存 Key 尽量绑定锁文件的哈希,只有当锁文件变化时才刷新缓存,否则能高命中率地复用。
  4. 拆分开发分支与发布分支流程
    • 开发分支(如 main)只跑“编译 + 单元测试 + 集成测试”流程;
    • 发布(Tag 或 release 分支)才走“构建 Release 可执行文件 / Docker 镜像 / 发布到生产环境”等流程。
    • 这样可以避免每次开发者合并 PR 都触发耗时的打包或部署,提高效率。
  5. 矩阵策略不要过度膨胀
    • 虽然可以把所有操作系统、所有语言版本、依赖版本都放进 Matrix,但会导致 Job 数量急剧增多,消耗更多并发额度和总时长。
    • 先分析哪些组合真的必要,再做分组。比如只在 Ubuntu 上跑全面集成测试,在 Windows/macOS 上跑编译和单元测试即可。
  6. Composite Actions 和 可复用步骤
    • 如果团队内部有固定的 Best Practice(如统一 Lint + 单元测试 + 安全扫描等),可以把这套步骤封装成一个 Composite Action,各项目直接复用。
    • Composite Action 的目录结构示例:

.github/actions/lint-and-test/ action.yml scripts/ run-lint.sh run-test.sh - 在 Workflow 中只需引用: yaml

  • name: Lint & Test uses: ./.github/actions/lint-and-test

      ```
    
  1. 定时任务(Cron)与手动触发并行
    • 当同时需要定时执行和手动触发时,写成:

on: push: { branches: [ main ] } schedule: - cron: '0 3 * * ' workflow_dispatch: {} - **Push** 会按 `paths-ignore` 或 `paths` 规则判断是否触发; - **定时** 和 **手动** 无视 `paths-ignore`,每次都会执行。 8. **分支策略与分支名称判断** - 常见用法:只有推送 Tag(如 `v*`)时才发布 Release,合并 PR 到 `main` 时只跑测试。 - 例子: yaml on: push: branches: [ main ] tags: [ 'v' ]

jobs: test: if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/') }} # 只在 push 到 main(非 Tag)时跑测试

release: if: ${{ startsWith(github.ref, 'refs/tags/') }} # 只在 Tag 推送时跑发布 ```#

六、小结#

  1. 核心概念要熟悉:Runner、Workflow、Job、Step、Action、Trigger。
  2. 灵活使用 paths / ****paths-ignore:可以精确控制何时触发 CI,节省资源。
  3. 缓存依赖:对常用语言都建议加速依赖、构建缓存。
  4. 矩阵构建要合理:覆盖必要环境,避免过度膨胀。
  5. 安全第一:所有敏感信息放在 Secrets,避免明文输出。
  6. 持续迭代与重用:将常见流程抽象为 Composite Action,方便维护、团队共享。 通过以上指南,你可以快速入门并掌握 GitHub Actions,在实际项目中实现从代码提交、测试、构建,到打包、发布的全自动化流水线,让开发效率和交付质量都得到显著提升。

📎 参考文章#