CI/CD 与 GitHub Actions 自动化¶
1. 一句话说明¶
CI/CD 是"代码提交后自动测试 + 自动部署"的工程实践,GitHub Actions 是 GitHub 内置的免费自动化引擎,让你的每次 push 都能自动跑测试、检查代码、构建镜像、发布文档。
2. 什么是 CI/CD¶
白话解释¶
想象你在餐厅当厨师: - CI(持续集成)= 每做一道菜都让质检员尝一口(自动测试),发现问题立即修正,不等到客人投诉 - CD(持续部署/交付)= 菜做好质检通过后,自动送到客人桌上(自动部署),不需要你亲自端盘子
没有 CI/CD 的痛点: - 代码改了不敢 push,怕把别人的功能搞坏 - 手动跑测试太慢,经常忘记跑 - 部署要登录服务器手动操作,容易出错 - 代码格式每个人不一样,review 时吵架
有了 CI/CD: - push 自动触发测试 → 坏了立刻知道 - PR 自动检查代码风格 → 统一规范 - merge 后自动部署 → 无需人工干预 - 文档改了自动发布 → 永远最新
3. GitHub Actions 核心概念¶
3.1 五大组件¶
# Workflow(工作流):一个自动化流程,由 YAML 文件定义
# 位置:.github/workflows/xxx.yml
# 白话:一份自动化任务清单
# Job(作业):Workflow 中的一组步骤
# 白话:任务清单中的一个大项(如"跑测试")
# Step(步骤):Job 中的一个具体操作
# 白话:大项里的一个小步骤(如"安装依赖")
# Runner(运行器):执行 Job 的虚拟机
# 白话:帮你干活的机器人(GitHub 免费提供 Ubuntu/macOS/Windows)
# Trigger(触发器):什么时候启动 Workflow
# 白话:机器人开始干活的信号(push/PR/定时/手动)
3.2 层级关系¶
Workflow (.yml 文件)
├── Job 1 (如:lint) ← 可并行
│ ├── Step 1: Checkout 代码
│ ├── Step 2: 安装依赖
│ └── Step 3: 跑 linter
├── Job 2 (如:test) ← 可并行
│ ├── Step 1: Checkout 代码
│ ├── Step 2: 安装 Python
│ ├── Step 3: 安装依赖
│ └── Step 4: 跑 pytest
└── Job 3 (如:deploy) ← 依赖 Job 1, 2 通过
├── Step 1: Checkout 代码
└── Step 2: 部署到服务器
4. YAML 语法基础¶
# YAML 基础语法(GitHub Actions 用 YAML 写配置)
# 键值对(冒号+空格)
name: my-workflow # 字符串值
timeout: 30 # 数字值
verbose: true # 布尔值
# 列表(短横线+空格)
steps:
- name: "第一步"
- name: "第二步"
# 嵌套(用缩进表示层级,2个空格)
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: "检出代码"
# 多行字符串(竖线 | 保留换行)
run: |
echo "第一行"
echo "第二行"
# 环境变量引用
env:
MY_VAR: hello
# 使用:${{ env.MY_VAR }}
# GitHub 上下文变量
# ${{ github.event_name }} 触发事件名
# ${{ github.ref }} 分支名
# ${{ github.sha }} 提交哈希
# ${{ secrets.MY_SECRET }} 仓库密钥
5. 实操教程¶
5.1 自动运行 pytest¶
# 文件:.github/workflows/test.yml
# 功能:每次 push 或 PR 自动运行 Python 测试
name: Run Tests # Workflow 名称(显示在 Actions 页面)
on: # 触发条件
push: # push 时触发
branches: [main, develop] # 只监听 main 和 develop 分支
pull_request: # PR 时触发
branches: [main]
jobs:
test: # Job 名称
runs-on: ubuntu-latest # 运行环境:最新 Ubuntu
strategy: # 矩阵策略:多版本测试
matrix:
python-version: ["3.9", "3.10", "3.11"] # 测试3个Python版本
steps:
# 步骤1:检出代码
- name: Checkout code
uses: actions/checkout@v4 # 使用官方 checkout action
# 步骤2:配置 Python
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }} # 矩阵变量
# 步骤3:缓存依赖(加速后续运行)
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip # 缓存 pip 目录
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
# 步骤4:安装依赖
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov # 安装测试框架
# 步骤5:运行测试
- name: Run tests with coverage
run: |
pytest tests/ --cov=src/ --cov-report=xml --cov-report=term
# --cov=src/ 统计 src 目录的覆盖率
# --cov-report=xml 输出 XML 格式(供后续工具使用)
# 步骤6:上传覆盖率报告
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
5.2 自动检查代码风格¶
# 文件:.github/workflows/lint.yml
# 功能:自动检查 Python 代码风格和类型
name: Code Quality
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
# Black 格式检查(只检查不修改)
- name: Check formatting with Black
run: |
pip install black
black --check --diff . # --check 不修改,只报错
# 如果格式不对,CI 会失败,提醒开发者格式化
# Ruff 快速 linting
- name: Lint with Ruff
run: |
pip install ruff
ruff check . # 检查代码问题
ruff format --check . # 检查格式
# MyPy 类型检查
- name: Type check with MyPy
run: |
pip install mypy
mypy src/ --ignore-missing-imports
5.3 自动构建 Docker 镜像¶
# 文件:.github/workflows/docker.yml
# 功能:代码合并到 main 后,自动构建并推送 Docker 镜像
name: Build Docker Image
on:
push:
branches: [main] # 只在 main 分支触发
tags: ['v*'] # 或打了 v 开头的 tag
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 登录 Docker Hub(需要在仓库 Settings > Secrets 添加凭证)
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }} # 仓库密钥
password: ${{ secrets.DOCKER_PASSWORD }}
# 提取元数据(自动生成 tag)
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: myuser/my-bioinfo-tool
tags: |
type=ref,event=branch # 分支名作为 tag
type=semver,pattern={{version}} # 语义化版本
# 构建并推送
- name: Build and push
uses: docker/build-push-action@v5
with:
context: . # Dockerfile 所在目录
push: true # 推送到 registry
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha # GitHub Actions 缓存加速
cache-to: type=gha,mode=max
5.4 自动部署文档¶
# 文件:.github/workflows/docs.yml
# 功能:文档更新后自动构建并发布到 GitHub Pages
name: Deploy Docs
on:
push:
branches: [main]
paths: ['docs/**', 'mkdocs.yml'] # 只有文档变化才触发
permissions:
contents: read
pages: write # 写 GitHub Pages 权限
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
# 安装 MkDocs(Python 文档生成器)
- name: Install MkDocs
run: |
pip install mkdocs mkdocs-material # Material 主题
pip install mkdocstrings[python] # 自动从代码生成文档
# 构建静态文档
- name: Build docs
run: mkdocs build # 生成 site/ 目录
# 部署到 GitHub Pages
- name: Deploy to GitHub Pages
uses: actions/upload-pages-artifact@v3
with:
path: site/
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
6. 生信 CI/CD 实践¶
6.1 自动测试 Pipeline¶
# 文件:.github/workflows/pipeline-test.yml
# 功能:每次改了分析脚本,自动用测试数据跑一遍
name: Test Bioinformatics Pipeline
on:
push:
paths: ['scripts/**', 'Snakefile', 'main.nf'] # 脚本变化才触发
jobs:
test-pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 安装 conda 环境
- name: Setup Miniconda
uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: bioinfo
environment-file: environment.yml # conda 环境文件
auto-activate-base: false
# 用测试数据跑 pipeline
- name: Run pipeline with test data
shell: bash -l {0} # 激活 conda
run: |
# 用小数据集快速测试
python scripts/run_pipeline.py \
--input test_data/ \
--output test_results/ \
--threads 2
# 验证输出文件
- name: Validate outputs
run: |
# 检查关键输出文件是否存在
test -f test_results/abundance_table.tsv
test -f test_results/diversity_metrics.csv
# 检查文件不为空
[ -s test_results/abundance_table.tsv ]
echo "Pipeline test passed!"
6.2 自动生成分析报告¶
# 文件:.github/workflows/report.yml
# 功能:定时运行分析并生成报告
name: Generate Weekly Report
on:
schedule:
- cron: '0 2 * * 1' # 每周一凌晨2点
workflow_dispatch: # 也可以手动触发
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install jupyter nbconvert pandas matplotlib
# 执行 Jupyter Notebook 生成报告
- name: Generate report
run: |
jupyter nbconvert --to html --execute \
notebooks/weekly_report.ipynb \
--output-dir reports/
# 上传报告为 artifact
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: weekly-report-${{ github.run_number }}
path: reports/
retention-days: 30 # 保留30天
7. 常用 Actions 市场¶
| Action | 用途 | 示例 |
|---|---|---|
actions/checkout@v4 | 检出仓库代码 | 几乎每个 workflow 都用 |
actions/setup-python@v5 | 配置 Python 环境 | 指定 Python 版本 |
actions/cache@v4 | 缓存依赖加速 | 缓存 pip/conda/npm |
actions/upload-artifact@v4 | 上传产物 | 测试报告、构建结果 |
actions/download-artifact@v4 | 下载产物 | 跨 Job 传递文件 |
docker/build-push-action@v5 | 构建推送 Docker | CI 构建镜像 |
conda-incubator/setup-miniconda@v3 | 配置 Conda | 生信环境管理 |
softprops/action-gh-release@v2 | 创建 Release | 自动发布版本 |
peaceiris/actions-gh-pages@v4 | 部署 GitHub Pages | 文档网站 |
codecov/codecov-action@v4 | 上传覆盖率 | 代码覆盖率报告 |
8. 面试怎么答¶
Q1:什么是 CI/CD?为什么生信也需要?¶
"CI 是持续集成——每次代码提交自动运行测试,确保新代码不会破坏已有功能。CD 是持续交付/部署——测试通过后自动发布。生信同样需要,因为分析 pipeline 也是代码,需要保证修改不会引入 bug。比如改了质控阈值,CI 自动用测试数据跑一遍 pipeline 验证输出正确性,比手动验证可靠得多。"
Q2:GitHub Actions 的基本结构是什么?¶
"一个 Workflow 由 YAML 文件定义,放在
.github/workflows/目录下。核心结构是:Trigger(触发条件,如 push/PR/定时)→ Job(作业,跑在 Runner 上)→ Step(步骤,可以是 shell 命令或复用社区 Action)。多个 Job 默认并行,也可以设置依赖顺序。"
Q3:你在项目中怎么用 CI/CD?¶
"在该 T2D 宏基因组项目中,我会设置:1)push 触发 pytest 跑单元测试,确保数据处理函数正确;2)PR 触发代码风格检查(Black + Ruff),保证代码质量;3)merge 到 main 后自动用测试数据跑一遍完整 pipeline 验证输出;4)文档更新自动部署到 GitHub Pages。这样团队协作时,任何人的修改都有自动验证。"
Q4:CI 跑得太慢怎么优化?¶
"几个常用策略:1)缓存依赖——用
actions/cache缓存 pip/conda 包,避免每次重装;2)路径过滤——paths参数限制只有相关文件变化才触发;3)矩阵策略并行——多个 Python 版本同时测试而非串行;4)分层测试——push 只跑快速单元测试,merge 时跑完整集成测试;5)使用更小的测试数据集。"
Q5:如何保护 CI 中的密钥(如数据库密码、API Key)?¶
"用 GitHub Secrets 管理。在仓库 Settings > Secrets and variables > Actions 中添加,在 YAML 中通过
${{ secrets.MY_SECRET }}引用。Secrets 在日志中自动打码,PR 从 fork 来的不会暴露 secrets。对于敏感操作(如部署),还可以设置 Environment 审批流程。"
9. 速查表¶
# ===== 触发条件 =====
on: push # push 时
on: pull_request # PR 时
on: [push, pull_request] # push 或 PR
on:
push:
branches: [main] # 指定分支
paths: ['src/**'] # 指定路径
tags: ['v*'] # 指定 tag
on:
schedule:
- cron: '0 0 * * *' # 定时(UTC)
on: workflow_dispatch # 手动触发
# ===== 常用 Runner =====
runs-on: ubuntu-latest # Ubuntu(最常用)
runs-on: macos-latest # macOS
runs-on: windows-latest # Windows
# ===== 矩阵策略 =====
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
os: [ubuntu-latest, macos-latest]
# ===== 条件执行 =====
if: github.event_name == 'push' # 只在 push 时
if: success() # 前面步骤成功
if: failure() # 前面步骤失败
if: always() # 无论如何都执行
# ===== Job 依赖 =====
jobs:
test:
...
deploy:
needs: test # 等 test 完成才跑
# ===== 常用环境变量 =====
${{ github.sha }} # 提交 SHA
${{ github.ref }} # 分支引用
${{ github.actor }} # 触发者
${{ github.repository }} # 仓库名
${{ secrets.GITHUB_TOKEN }} # 内置 token
10. 延伸资源¶
| 资源 | 说明 |
|---|---|
| GitHub Actions 官方文档 | 最权威参考 |
| Actions Marketplace | 社区 Action 市场 |
| act | 本地运行 GitHub Actions |
| GitHub Actions for Bioinformatics | Snakemake CI Action |
| nf-core CI 模板 | nf-core 如何做 CI |
| GitHub Skills | 交互式学习 GitHub |
免费额度:公开仓库无限免费;私有仓库每月 2000 分钟(Linux)。生信学术项目通常是公开仓库,完全免费。