Python CI with GitHub Actions(整理版)
原始资料: Building and testing Python created: 2026-07-02 16:58 整理说明: 本版本结合原始笔记和 GitHub Docs 原始教程重排、补全和翻译,保留常用英文术语;原教程截图已下载到本地附件目录。
YAML 常用字段速查
| 字段 | 常见写法 | 作用 |
|---|---|---|
name |
name: Python package |
workflow 在 GitHub Actions 页面中的名称。 |
run-name |
run-name: test on ${{ github.ref }} |
单次 workflow run 的显示名。 |
on |
on: [push, pull_request] |
定义触发 workflow 的事件。 |
permissions |
contents: read |
限制 GITHUB_TOKEN 权限,发布或 OIDC 时尤其重要。 |
jobs |
jobs: build: |
workflow 中的 job 集合。 |
<job_id> |
build: |
job 的内部 ID,可以被 needs 引用。 |
runs-on |
ubuntu-latest |
指定 job 运行的 runner。 |
strategy.matrix |
python-version: ["3.11", "3.12"] |
为不同版本、系统或配置生成多组 job。 |
matrix.exclude |
exclude: [{ os: windows-latest, python-version: "3.11" }] |
从 matrix 中排除不想运行的组合。 |
steps |
steps: |
job 内顺序执行的步骤列表。 |
uses |
actions/checkout@v6 |
调用已有 action。 |
with |
python-version: "3.x" |
给 action 传入参数。 |
run |
pytest tests/ |
在 runner shell 中执行命令。 |
env |
PYTHONWARNINGS: error |
设置环境变量。 |
needs |
needs: build |
指定 job 依赖关系。 |
if |
if: ${{ always() }} |
控制 step 或 job 是否执行。 |
continue-on-error |
true |
允许某个 step 失败但不中断整个 job。 |
timeout-minutes |
10 |
限制 job 或 step 最长运行时间。 |
Python CI 常用字段速查
| 场景 | 推荐字段或命令 | 作用 |
|---|---|---|
| checkout 代码 | uses: actions/checkout@v6 |
把仓库代码拉到 runner。 |
| 设置 Python | uses: actions/setup-python@v5 |
选择 CPython 或 PyPy,并加入 PATH。 |
| 固定 Python 版本 | python-version: "3.12" |
使用明确版本,避免 runner 默认版本变化。 |
| 使用版本范围 | python-version: "3.x" |
获取最新的 Python 3 minor release。 |
| 多版本测试 | python-version: ["3.9", "3.11", "3.13"] |
通过 matrix 覆盖多个 Python 版本。 |
| 测 PyPy | python-version: "pypy3.10" |
验证项目在 PyPy 解释器上的兼容性。 |
| 指定架构 | architecture: "x64" |
设置解释器架构,默认通常是 x64。 |
| pip 缓存 | cache: "pip" |
让 setup-python 缓存依赖,加速 CI。 |
| 依赖文件缓存键 | cache-dependency-path: requirements.txt |
指定依赖锁文件或需求文件。 |
| 安装基础构建工具 | python -m pip install --upgrade pip setuptools wheel |
更新 Python packaging 相关基础工具。 |
| 安装项目依赖 | pip install -r requirements.txt |
安装项目运行和测试依赖。 |
| pytest 测试 | pytest tests/ |
运行测试套件。 |
| 覆盖率 | pytest --cov=<package> --cov-report=xml |
生成 coverage 报告。 |
| JUnit 报告 | --junitxml=junit/test-results.xml |
生成可上传或分析的测试结果。 |
| Ruff lint | ruff check --output-format=github |
在 GitHub UI 中显示 lint annotation。 |
| Ruff format check | ruff format --diff |
检查格式差异。 |
| tox | tox -e py |
用 tox 管理测试环境,适合复杂项目。 |
| 上传测试结果 | uses: actions/upload-artifact@v4 |
保存 JUnit、coverage、日志等产物。 |
| PyPI 发布 | pypa/gh-action-pypi-publish |
CI 通过后发布 Python package。 |
内容简要概括
这篇笔记整理了如何用 GitHub Actions 为 Python 项目创建 CI workflow,覆盖从模板创建、选择 Python/PyPy 版本、安装依赖,到运行 pytest、Ruff、tox 和上传测试产物的常见做法。核心原则是用 actions/setup-python 明确指定 Python 版本,并用 strategy.matrix 在多个 Python 版本或操作系统上重复执行同一套测试。对 Python package 项目,还可以在 CI 通过后构建 artifact,并通过 Trusted Publishing 发布到 PyPI。
GitHub Actions、Python CI、YAML、actions/setup-python、actions/checkout、strategy.matrix、python-version、PyPy、pip、requirements.txt、pytest、pytest-cov、Ruff、tox、artifact
目录
- 1. Python CI 的整体流程
- 2. 使用 Python workflow template
- 3. 指定 Python 版本
- 4. 安装依赖与缓存
- 5. 测试、lint 与格式检查
- 6. 保存测试结果和发布到 PyPI
- 7. 原始教程要点
- 8. 可复用模板汇总
1. Python CI 的整体流程
Python CI 的基本目标是:每次 push 或 PR 发生时,自动创建干净的 runner 环境,安装项目依赖,运行测试和静态检查,并输出可追踪的结果。
一个典型 Python CI workflow 包含这些步骤:
- 用
actions/checkout拉取仓库代码。 - 用
actions/setup-python指定 Python 或 PyPy 版本。 - 升级
pip,安装setuptools、wheel、项目依赖和测试工具。 - 运行
pytest、Ruff、tox等检查。 - 需要时上传 JUnit XML、coverage HTML/XML、日志或构建产物。
- 对 package 项目,可以在 release 事件后构建分发包并发布到 PyPI。
2. 使用 Python workflow template
GitHub 提供了 Python workflow template。如果仓库里已经有至少一个 .py 文件,GitHub 通常会推荐 Python 相关模板。
操作路径:
- 打开 GitHub repository 首页。
- 点击仓库顶部导航中的 Actions。

- 如果仓库已经有 workflow,点击 New workflow。
- 在 Choose a workflow 页面搜索
Python application。 - 在
Python applicationworkflow 上点击 Configure。 - 按项目需要修改 workflow,例如 Python 版本、依赖安装命令、测试命令。
- 点击 Commit changes,GitHub 会把
python-app.yml加入.github/workflows目录。
模板适合快速起步;如果项目需要多 Python 版本、多 OS、coverage、Ruff 或 PyPI 发布,通常要继续定制。
3. 指定 Python 版本
GitHub-hosted runners 自带工具缓存,其中包含 Python 和 PyPy。推荐使用 actions/setup-python,因为它会从 runner 的 tools cache 中查找指定版本,并把对应解释器加入 PATH。如果目标版本不在缓存中,setup-python 会按 action 规则下载并设置合适版本。
不要依赖 runner 默认 Python 版本。默认版本会随 runner 镜像变化而变化,可能让 CI 在未来某天突然表现不一致。
3.1 为什么要加 PyPy
PyPy 是 Python 的另一种解释器实现,和常见的 CPython 不同。它使用 JIT 编译策略,某些长时间运行的纯 Python 程序可能更快。
在 CI 中加入 PyPy 的价值主要是兼容性检查:
- 验证代码是否依赖了 CPython 特有行为。
- 发现 C extension、二进制依赖或运行时假设带来的差异。
- 对宣称支持 PyPy 的 library/package,提供真实测试保障。
如果项目大量依赖只支持 CPython 的扩展包,或者根本不打算支持 PyPy,就不必强行加入 PyPy matrix。
3.2 多 Python 版本测试
多版本测试适合 library、package 或需要支持多个 Python 版本的项目。
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python -c "import sys; print(sys.version)"
字段解释:
| 字段 | 说明 |
|---|---|
strategy.matrix.python-version |
定义要测试的 Python/PyPy 版本列表。 |
${{ matrix.python-version }} |
在每个 job 变体中读取当前 Python 版本。 |
actions/setup-python@v5 |
安装并激活当前 matrix 指定的 Python。 |
Display Python version |
打印实际解释器版本,便于确认 CI 环境。 |
3.3 指定单个 Python 版本
单版本 CI 适合应用项目、课程作业或只需要保证当前主版本稳定的项目。
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
architecture: "x64"
- name: Display Python version
run: python -c "import sys; print(sys.version)"
字段解释:
| 字段 | 说明 |
|---|---|
python-version: "3.x" |
使用最新 Python 3 minor release;也可以写成 "3.12" 这类精确版本。 |
architecture: "x64" |
指定解释器架构,通常可以省略,因为默认就是 x64。 |
uses: actions/setup-python@v5 |
这里的 v5 是 action 版本,不是 Python 版本。 |
3.4 排除特定 matrix 组合
当某些 OS 和 Python 版本组合不需要测试,或者已知暂时不支持时,可以用 exclude 排除。
name: Python package
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.11", "3.13", "pypy3.10"]
exclude:
- os: macos-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.11"
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python -c "import sys; print(sys.version)"
字段解释:
| 字段 | 说明 |
|---|---|
matrix.os |
定义多个 runner 系统。 |
runs-on: ${{ matrix.os }} |
每个 matrix 变体使用对应系统运行。 |
exclude |
删除某些不需要的 matrix 组合。 |
4. 安装依赖与缓存
GitHub-hosted runners 已经安装了 pip,但通常仍建议先升级 packaging 相关工具。
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install dependencies
run: python -m pip install --upgrade pip setuptools wheel
如果项目使用 requirements.txt:
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
字段解释:
| 字段 | 说明 |
|---|---|
python -m pip |
确保调用的是当前 Python 解释器对应的 pip。 |
pip install -r requirements.txt |
按项目依赖文件安装依赖。 |
cache: "pip" |
让 setup-python 自动缓存 pip 依赖。 |
cache-dependency-path |
依赖文件不在默认位置时,用它指定路径。 |
如果需要更细粒度控制缓存,可以使用 actions/cache。不过对常见 Python 项目,先用 setup-python 自带的 cache: "pip" 更简单。
5. 测试、lint 与格式检查
5.1 使用 pytest 和 pytest-cov
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Test with pytest
run: |
pytest tests/ --doctest-modules --junitxml=junit/test-results.xml --cov=your_package --cov-report=xml --cov-report=html
字段解释:
| 字段 | 说明 |
|---|---|
pytest tests/ |
运行 tests/ 目录下的测试。 |
--doctest-modules |
同时检查 docstring 中的 doctest。 |
--junitxml=... |
输出 JUnit XML,方便上传和集成测试报告。 |
--cov=your_package |
指定要统计覆盖率的 package。 |
--cov-report=xml |
生成 Cobertura 兼容 XML 覆盖率报告。 |
--cov-report=html |
生成 HTML 覆盖率报告,适合上传为 artifact。 |
5.2 使用 Ruff 做 lint 和 format check
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install Ruff
run: pipx install ruff
- name: Lint code with Ruff
run: ruff check --output-format=github --target-version=py39
- name: Check code formatting with Ruff
run: ruff format --diff --target-version=py39
continue-on-error: true
continue-on-error: true 适合在刚引入格式检查时使用:它会显示格式差异,但不会让整个 workflow 失败。等代码格式修好后,可以移除这个选项,让 CI 真正阻止新的格式问题。
5.3 使用 tox
tox 适合依赖、测试命令或环境矩阵比较复杂的项目。GitHub Actions 中可以把 Python 版本交给 matrix 管理,再让 tox -e py 使用当前 PATH 中的 Python。
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.9", "3.11", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install tox
run: pip install tox
- name: Run tox
run: tox -e py
6. 保存测试结果和发布到 PyPI
6.1 上传测试结果 artifact
测试失败时也可能需要保留测试报告,所以上传 artifact 的 step 常配合 if: ${{ always() }} 使用。
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Test with pytest
run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
- name: Upload pytest test results
uses: actions/upload-artifact@v4
with:
name: pytest-results-${{ matrix.python-version }}
path: junit/test-results-${{ matrix.python-version }}.xml
if: ${{ always() }}
6.2 发布到 PyPI
发布到 PyPI 不应该和普通 push CI 混在一起。更常见的做法是在 GitHub release 发布时触发,并使用 PyPI Trusted Publishing,避免手动保存 API token。
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
release-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Build release distributions
run: |
python -m pip install build
python -m build
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/
pypi-publish:
runs-on: ubuntu-latest
needs: release-build
permissions:
id-token: write
environment:
name: pypi
steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v5
with:
name: release-dists
path: dist/
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
关键点:
needs: release-build表示发布 job 必须等构建 job 成功后再执行。permissions.id-token: write是 Trusted Publishing 所需权限。environment: pypi可以配合 GitHub environment protection 做发布保护。- 生产级 workflow 建议 pin action 到 commit SHA,降低上游 action 被改动带来的供应链风险。
7. 原始教程要点
GitHub Docs 的 Python CI 教程主要强调:
- Python workflow template 可以快速生成
.github/workflows/python-app.yml。 - GitHub-hosted runners 自带 Python、PyPy 和
pip,但 CI 中仍应显式使用actions/setup-python。 - 多版本测试通过
strategy.matrix实现,既可以覆盖多个 Python 版本,也可以覆盖多个 OS。 setup-python支持 pip 缓存,默认会查找requirements.txt、Pipfile.lock或poetry.lock等依赖文件。- 测试命令可以复用本地命令,例如
pytest、ruff、tox。 - 测试报告、coverage、日志和截图等都可以用 artifact 保存,便于失败后排查。
- 发布到 PyPI 时优先考虑 Trusted Publishing,不要在仓库中硬编码或提交 API token。
8. 可复用模板汇总
8.1 推荐起步版 Python CI
name: Python CI
on:
push:
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Test
run: |
pytest tests/ --junitxml=junit/test-results.xml --cov=your_package --cov-report=xml --cov-report=html
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.python-version }}
path: |
junit/
htmlcov/
coverage.xml
if: ${{ always() }}
8.2 加 Ruff 的版本
name: Python CI
on:
push:
pull_request:
permissions:
contents: read
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov ruff
- name: Lint
run: ruff check --output-format=github .
- name: Format check
run: ruff format --check .
- name: Test
run: pytest tests/