9.1 为什么 CI 一定要跑 Ruff
pre-commit 钩子在本地跑,但依赖开发者不主动 --no-verify 绕过。CI 是最后一道强制防线——代码进主干前,必须在 Pull Request 上通过 Ruff 检查。CI 层面的好处:
- 统一版本(不依赖开发者本地装了什么)
- 输出结构化日志、PR 评论、状态检查
- 阻塞合并(Required status check)
9.2 官方 GitHub Action
Astral 官方维护 astral-sh/ruff-action,一行能搞定:
# .github/workflows/lint.yml
name: Lint
on:
push:
branches: [main]
pull_request:
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
version: "0.11.3" # 锁定版本
args: "check --output-format=github"
9.3 --output-format=github:原生注释
在 PR 页面,Ruff 会把每个违规显示成 代码行上的 annotation——点击直接定位到源码位置。实现靠一个特殊的输出格式:
$ ruff check --output-format=github .
# ::error file=app.py,line=12,col=5,title=F401::`os` imported but unused
这种 ::error ... 注释 GitHub Actions 会自动识别并转换成 PR 代码行评论。其他格式:
- text
- 默认,人类可读。
- full
- 带完整诊断上下文(多行)。
- json / json-lines
- 机器可读,自定义 dashboard 时用。
- junit
- Jenkins / Azure DevOps 的 JUnit XML 格式。
- gitlab
- GitLab Code Quality 报告格式。
- pylint
- 兼容 pylint 输出(便于老工具链接入)。
- sarif
- Static Analysis Results Interchange Format,GitHub Advanced Security 可读。
9.4 完整 workflow:Lint + Format + 测试
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
args: "check --output-format=github"
- uses: astral-sh/ruff-action@v3
with:
args: "format --check"
test:
needs: lint # lint 过了再跑测试
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
version: "0.5.x"
- run: uv sync --frozen
- run: uv run pytest
9.5 --statistics:规则分布
大仓初次接入 Ruff 想知道"到底哪些规则最高频":
$ ruff check --statistics .
12 F401 [*] unused-import
45 E501 line-too-long
8 B006 [*] mutable-argument-default
3 F841 [*] unused-variable
把这个输出存成 CI artifact,可以跟踪"技术债"随 commit 的下降曲线。
9.6 缓存 Ruff 二进制
ruff-action 默认会缓存 ruff 二进制。若手动用 pip install ruff 方式,建议加缓存:
- uses: actions/cache@v4
with:
path: ~/.cache/ruff
key: ruff-${{ hashFiles('pyproject.toml') }}
9.7 只检查变更文件(大仓提速)
几十万行的仓库 Ruff 也只要几秒,但如果想更快,可以只检查 PR 变更的文件:
- name: Get changed files
id: changed
uses: tj-actions/changed-files@v45
with:
files: "**/*.py"
- uses: astral-sh/ruff-action@v3
if: steps.changed.outputs.any_changed == 'true'
with:
args: "check --output-format=github ${{ steps.changed.outputs.all_changed_files }}"
9.8 GitLab CI
# .gitlab-ci.yml
ruff:
image: ghcr.io/astral-sh/ruff:0.11.3
script:
- ruff check --output-format=gitlab . > code-quality.json
- ruff format --check .
artifacts:
reports:
codequality: code-quality.json
Ruff 官方发布 Docker 镜像 ghcr.io/astral-sh/ruff,镜像很小(几 MB,纯静态二进制)。
9.9 Jenkins / 其他 CI
# Jenkinsfile
stage('Ruff') {
steps {
sh 'pipx install ruff==0.11.3'
sh 'ruff check --output-format=junit . > ruff.xml || true'
junit 'ruff.xml'
}
}
9.10 Required status checks(强制合入前通过)
GitHub 仓库 → Settings → Branches → 添加 main 分支保护规则 → 勾选 Require status checks to pass before merging → 选中 "ruff" job。这样任何未跑过 Ruff 的 PR 都无法合并。
本地 pre-commit(第一道,防低级错误)+ CI Ruff(第二道,不可绕过)+ Branch Protection(第三道,必须通过)。三层下来,垃圾代码基本进不了主干。
9.11 小结
- 用
astral-sh/ruff-action一行接入 GitHub Actions。 --output-format=github让 Ruff 成为 PR 内联评论;GitLab/Jenkins 分别用gitlab/junit格式。- CI 与 pre-commit 必须同时存在——前者是防线,后者是便利。
- Branch Protection 强制 CI 通过,杜绝"合并即破坏"。