CI/CD 是现代软件开发的基础设施。GitHub Actions 作为 GitHub 生态的原生 CI/CD 方案,上手简单、社区活跃,已经成为我所有项目的标配。本文分享我在实际项目中积累的配置经验和最佳实践。
基础概念
一个 GitHub Actions 工作流由以下概念构成:
- Workflow:定义在
.github/workflows/目录下的 YAML 文件,一个仓库可以有多个工作流 - Event:触发工作流的事件,如 push、pull_request、schedule(定时)、workflow_dispatch(手动)
- Job:工作流中的一个执行单元,多个 Job 默认并行执行
- Step:Job 中的一个执行步骤,可以运行命令或使用 Action
- Runner:执行 Job 的虚拟环境(ubuntu-latest、macos-latest、windows-latest)
前端项目部署:静态站点到 GitHub Pages
这是最常见的场景——将静态网站自动部署到 GitHub Pages:
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
关键点说明:
permissions: contents: write是必需的,否则 Action 无法推送部署分支npm ci比npm install更快且更确定,适合 CI 环境peaceiris/actions-gh-pages是社区最常用的 GitHub Pages 部署 Action
Rust 项目:多平台构建与发布
对于我的开源库 MarkdownKit,每次打 tag 时需要自动构建多平台二进制文件并发布到 GitHub Release:
name: Release
on:
push:
tags: ['v*']
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build release binary
run: cargo build --release
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: markdownkit-${{ matrix.os }}
path: target/release/markdownkit*
release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: markdownkit-*/markdownkit*
generate_release_notes: true
这里使用了矩阵构建(matrix strategy),同一个 Job 会在三个操作系统上并行执行,最后在 release Job 中汇总产物。
缓存优化:加速 CI
缓存是 CI 性能优化的关键。不同语言的缓存策略:
# Node.js: 缓存 node_modules
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Rust: 缓存 target/ 目录和 cargo registry
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
最佳实践总结
- 使用 workflow_dispatch:除了自动触发外,总是加上手动触发选项,方便排查和重试
- 锁定 Action 版本:使用
@v4而非@main,避免上游改动破坏你的工作流 - 最小权限原则:通过
permissions:字段显式声明所需权限,而非使用默认的读写全部权限 - 合并 CI 步骤:将测试、lint、构建合并到一个 Job 中(如果速度可以接受),减少 Job 之间的等待和 artifact 传递开销
- 善用 concurrency:使用
concurrency.group确保同一分支的旧工作流在新 push 时自动取消
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
这套配置已经在我 4 个开源项目中稳定运行了一年多,希望对你有所帮助 🚀