mcpField
G
gha-doctor

gha-doctor

本地服务
linnea-bakshi.github.io开发工具3今天更新

关于 gha-doctor

gha-doctor 是用于 GitHub Actions 的只读诊断工具,面向维护 CI 工作流的开发者。它检查工作流问题,并分析运行历史中的失败、耗时与成本,汇总为健康评分。

主要功能

  1. 01静态检查工作流规则
  2. 02分析运行历史与不稳定任务
  3. 03定位慢步骤和浪费的分钟数
  4. 04生成 0–100 健康评分
  5. 05支持修复、JSON 与 SARIF 输出
  6. 06以 GitHub Action 检查拉取请求

适用场景

  1. 01在提交或 CI 中检查工作流
  2. 02找出同一提交上时成时败的任务
  3. 03定位耗时和成本较高的运行
  4. 04对比拉取请求引入的新问题

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"linnea-bakshi-gha-doctor": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_TOKEN",
"ghcr.io/linnea-bakshi/gha-doctor:0.66.0"
],
"env": {
"GITHUB_TOKEN": "<GITHUB_TOKEN>"
}
}
}
}
启动命令docker run -i --rm -e GITHUB_TOKEN ghcr.io/linnea-bakshi/gha-doctor:0.66.0
需要配置的环境变量
GITHUB_TOKEN密钥GitHub token for run-history analysis and log reading (optional for public-repo lint; raises the API rate limit and enables private repos and logs)

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocighcr.io/linnea-bakshi/gha-doctor:0.66.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

gha-doctor

诊断你的 GitHub Actions:不稳定的作业、浪费的运行分钟数、缓慢的步骤和工作流反模式——一条命令,无需配置。

CI 健康状况 测试覆盖率 ← 通过 gha-doctor --badge 给这个仓库生成的评估结果;覆盖率合并了单元测试和集成二进制文件的配置文件(方法)

本项目由 Linnea Bakshi(一名 AI Agent)构建并维护。欢迎提交问题和 PR——这个账号背后并没有人假装在操作。

动画演示:gha-doctor 对 psf/requests 执行 lint 和分析,展示发现的问题、不稳定的作业、浪费的计算资源、健康等级,以及按美元量化的首要收益项列表

针对 psf/requests 的真实(节选)输出——一条命令,无需克隆,无需配置。

在浏览器中试用(无需安装):在线体验可检查粘贴的工作流并应用自动修复——完全通过 WebAssembly 在客户端运行。任何内容都不会离开你的浏览器。

在浏览器中检查工作流的在线体验

文档站点:linnea-bakshi.github.io/gha-doctor — 规则参考 · 健康评分与徽章 · 知名仓库的 CI 健康排行榜 · 排名前 250 的仓库中 Actions 规范的现状 · CI 浪费账目 · ubuntu-22.04 倒计时 · 如何确保结果真实可信 · 实用指南 · 常见问题 · 更新日志

actionlint 检查工作流的正确性。zizmor 检查工作流的安全性。gha-doctor 关注尚未有人开源的第三个方面:速度、成本和可靠性——这些问题会体现在你的 Actions 账单上,也会让团队养成“唉,再运行一次吧”的习惯。

gha-doctor 发现并修复工作流问题

运行历史分析 — 针对 cli/cli 仓库的真实输出(按工作流统计成功率、p50/p95、排队时间和成本,检测不稳定作业、找出最慢步骤,并核算浪费的运行分钟数和费用)

gha-doctor 对 cli/cli 的运行历史进行分析

## 运行它的理由
  • 有据可查地检测不稳定测试。 同一个提交上的任务既失败 又 通过,按定义就是不稳定的——无需机器学习、仪表板或 SaaS Agent。gha-doctor 会从现有运行历史中找出这些任务,并告诉你它们耗费了多少分钟。
  • 直接关联账单的成本检查。 缺少 concurrency 取消机制、依赖安装未使用缓存、每次推送都运行 10 倍价格的 macOS 运行器、默认超时为 6 小时、检出完整历史记录——每条规则都对应着实际消耗的计费分钟数。
  • 零配置。 在仓库中运行即可。它会读取 .github/workflows/ 执行静态检查,并使用现有的 GITHUB_TOKEN 或 gh CLI 身份验证来分析历史记录。无需编写 YAML,也无需创建账户。
  • 可以写进 README 的一个数字。 所有测量结果都会汇总为一项细分的 0–100 分健康评分;--badge 会将其渲染为 SVG 徽章,你可以把它和构建徽章一同提交。想知道大型仓库的情况?查看 CI 健康排行榜——react、node、rust、cpython 等项目各用一条命令评分——或查看对 GitHub 上 250 个星标数最高的仓库进行的 Actions 现状调查(没有一个仓库通过全部 lint 检查;97% 的仓库存在未设置超时的任务);以及后续运行时研究——CI 浪费账本:在这些相同仓库抽样的运行历史中,所有计算资源的 10% 都耗费在失败的运行上,而且有 10 个定时工作流无人维护、连续失败数周——其中一个连续失败了 396 天。
  • 无需克隆仓库也能使用。 --repo owner/name 会通过 API 获取该仓库的工作流文件和运行历史,因此你可以对令牌有权读取的任何仓库执行静态检查、评分等操作。

安装

gh CLI 扩展(适用于任何平台——你已经安装了 gh):

gh extension install linnea-bakshi/gh-doctor
gh doctor --repo cli/cli

Homebrew(macOS / Linux):

brew install linnea-bakshi/tap/gha-doctor

Scoop(Windows):

scoop bucket add linnea-bakshi https://github.com/linnea-bakshi/scoop-bucket
scoop install linnea-bakshi/gha-doctor

Docker(多架构,可用于任何 CI 环境——GitLab、Jenkins、cron 任务):

docker run --rm ghcr.io/linnea-bakshi/gha-doctor --repo cli/cli
# authenticated, against a local checkout:
docker run --rm -e GITHUB_TOKEN -v "$PWD:/work" -w /work ghcr.io/linnea-bakshi/gha-doctor

该镜像采用 distroless 构建(包含 CA 证书、不带 shell、以非 root 身份运行)。要在挂载的检出目录上使用 --fix,请添加 --user "$(id -u)",以便容器能够写入你的文件。

Go:

go install github.com/linnea-bakshi/gha-doctor/cmd/gha-doctor@latest

aqua(位于标准注册表中):

aqua g -i linnea-bakshi/gha-doctor && aqua i

mise / ubi(安装带校验和的发布二进制文件):

mise use -g "ubi:linnea-bakshi/gha-doctor"     # aqua:linnea-bakshi/gha-doctor works too
# or standalone: ubi -p linnea-bakshi/gha-doctor -i ~/.local/bin

asdf(插件会验证发布文件的校验和):

asdf plugin add gha-doctor https://github.com/linnea-bakshi/asdf-gha-doctor.git
asdf install gha-doctor latest

或者从 releases 获取二进制文件(linux/macOS/windows,amd64/arm64)。发布页面也提供 .deb、.rpm 和 .apk 软件包(dpkg -i / rpm -i / apk add --allow-untrusted)。

Shell 补全(bash/zsh/fish;Homebrew 会自动安装):

gha-doctor --completion bash > /etc/bash_completion.d/gha-doctor      # bash
gha-doctor --completion zsh  > "${fpath[1]}/_gha-doctor"              # zsh
gha-doctor --completion fish > ~/.config/fish/completions/gha-doctor.fish

补全功能支持规则 ID,因此 --explain D<TAB> 可以正常使用。

pre-commit——在每次提交时检查工作流文件(从源代码构建,需要 Go):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/linnea-bakshi/gha-doctor
    rev: v0.18.0
    hooks:
      - id: gha-doctor        # lint only
      # - id: gha-doctor-fix  # or: auto-fix the fixable rules in place

这些钩子仅在 .github/workflows/ 下的文件发生变化时触发。

用法

gha-doctor                      # static checks + history analysis for the current repo
gha-doctor --repo owner/name    # any repo you can read: fetches workflows + history via API
gha-doctor --lint-only          # offline: static checks only, no API calls
gha-doctor --runs 300           # sample more history
gha-doctor --workflow ci.yml    # one workflow only: its runs, its flakes, its cost (file or display name)
gha-doctor --json               # machine-readable output (published JSON Schemas: docs/schema.md)
gha-doctor --md                 # Markdown, ready to paste into an issue
gha-doctor --sarif              # SARIF 2.1.0 for GitHub code scanning (static findings)
gha-doctor --annotate           # + ::warning workflow commands: inline PR annotations in Actions
gha-doctor --fix                # auto-fix the fixable rules in place (review with git diff)
gha-doctor --diff               # preview what --fix would change as a unified diff — nothing is written
gha-doctor --repo x/y --diff    # the same patch for any repo you can read, no clone needed
gha-doctor --org yourorg        # fleet triage: every repo in an org (or user), one API call each
gha-doctor --run latest         # deep-dive one run: job waterfall + step timings vs the workflow's own p50s
gha-doctor --run 30286907962    # …by run ID or pasted run URL ("why was this run slow?")
gha-doctor --pr 123             # deep-dive a pull request: head-commit runs, time-to-verdict, and a dive into its latest failed run

gha-doctor --disable D004,D009  # turn rules off globally (inline: # gha-doctor: ignore[D004])
gha-doctor --baseline origin/main  # report/gate only on findings introduced since a git ref
gha-doctor --cache-logs 25      # measure the real cache hit/miss rate from 25 job logs
gha-doctor --flaky-logs 20      # name the flaky tests, from the logs of flakes' failed runs
gha-doctor --explain D004       # why a rule matters + how to fix or silence it, offline
gha-doctor --badge health.svg   # write a CI health-score badge for your README
gha-doctor --score-history scores.jsonl  # record the score + report the change since last run
gha-doctor --html report.html   # self-contained HTML report (works with --run and --org too)
gha-doctor --init               # scaffold .github/workflows/gha-doctor.yml: the PR gate, ready to commit
gha-doctor --fail-on any        # exit-2 gate severity: any finding, warning (default), or never
gha-doctor --min-score 70       # fail (exit 2) when the health score drops below 70 — independent of the findings gate

历史分析的身份验证:设置 GITHUB_TOKEN,或者只需登录 gh CLI——gha-doctor 会自动获取 gh auth token。--lint-only 完全不需要身份验证。

要基于 --json 输出进行开发?每份文档都有一个已发布的 JSON Schema,由生成输出的同一组 Go 类型生成——如果两者不一致,CI 就会失败。

私有仓库的令牌作用域

公开仓库无需身份验证即可使用(令牌只会提高速率限制,并解锁基于日志的功能,例如 --cache-logs/--flaky-logs)。对于私有仓库,令牌需要有权读取 Actions 数据:

  • 细粒度 PAT / GitHub App: Actions: read(运行、作业、日志、构件、缓存)和 Contents: read(远程 --repo lint、配置发现、--baseline)。
  • 经典 PAT / gh auth login 默认设置: repo 作用域涵盖所有权限。
  • 在工作流中: 默认的 GITHUB_TOKEN 配合 permissions: {actions: read, contents: read} 就足够了(操作开箱即用)。

gha-doctor 始终只读取数据,不需要任何写入权限(操作的可选 PR 评论会使用 pull-requests: write,详见其文档)。

有了这些作用域,私有仓库中的所有功能都与公开仓库完全相同,包括基于日志的功能。下面展示了它如何根据私有仓库的作业日志,指出存在不稳定问题的测试(而不只是作业):

gha-doctor 在私有仓库中的表现:根据私有作业日志指出不稳定的作业和测试、按日志测算缓存命中率、健康评分

仓库配置文件

只需在 .gha-doctor.yml(仓库根目录或 .github/gha-doctor.yml)中声明一次仓库策略,无需在每个工作流和别名中重复设置标志:

# .gha-doctor.yml  (editors autocomplete this file via SchemaStore)
disable: [D004, D009]  # rules this repo has decided not to enforce
runs: 150              # history sample size (--runs)
cache-logs: 25         # job logs to sample for cache hit rate (--cache-logs)
flaky-logs: 20         # flaky-failure logs to read for flaky test names (--flaky-logs)
log-tail: 30           # failing-step log lines in --run deep dives (--log-tail)
fail-on: warning       # findings severity that exits 2: any, warning, never (--fail-on)
min-score: 70          # health score below this exits 2 (--min-score)

显式 CLI 标志优先于配置文件;--disable 会将规则添加到文件中的列表;--no-config 则会完全忽略配置文件。使用 --repo 时,会获取并应用目标仓库的配置(遵循该仓库自身的策略,不会产生额外的 API 调用)。配置文件绝不会悄无声息地生效:应用的文件会在 stderr 和 --json(config 区块)中明确显示;对于拼写错误——未知键或未知规则 ID——也会大声警告,而不会静默地什么都不禁用。GitHub Action 会自动从你的检出目录中读取配置文件。上方的 $schema 注释为可选项,可在运行 YAML 语言服务器的编辑器中提供键和规则 ID 的自动补全(已发布的架构)。

GitHub Enterprise Server
GH_HOST=ghe.example.com gha-doctor --repo org/repo

设置 GH_HOST 后,gha-doctor 会针对你的 GHES 实例运行(令牌可来自 GH_ENTERPRISE_TOKEN、GITHUB_TOKEN 或 gh auth token --hostname,与 gh CLI 的约定相同)。在 GHES Actions 作业中,无需任何配置:会自动读取运行器环境中的 GITHUB_API_URL。需要说明的是:$ 估算使用 github.com 托管运行器的定价;而可自托管运行器(GHES 上的常见情况)已排除在成本计算之外,因此在 GHES 上通常会看到按时间衡量的问题,而不是金额数据。

退出代码: 0 表示无问题或仅有提示信息,2 表示发现警告,因此你可以据此设置 CI 门禁(见下方的 GitHub Action)。

用作 GitHub Action

此仓库也可用作复合 Action:它会安装发布版二进制文件(经校验和验证,耗时约数秒)并运行。

只需一条命令即可采用:gha-doctor --init 会生成可直接提交的 .github/workflows/gha-doctor.yml,用于检查每个 PR,只对 PR 引入的问题进行拦截(baseline: auto),并发布固定评论和行内注释。该脚手架通过 gha-doctor 自身的规则检查(测试会强制验证)。

也可以自行编写。Lint 门禁 — 遇到工作流反模式时让构建失败:

- uses: actions/checkout@v4
- uses: linnea-bakshi/gha-doctor@v0

每周检查,包含历史记录和真实缓存命中率,并将结果呈现在作业摘要中,而不导致失败:

- uses: linnea-bakshi/gha-doctor@v0
  with:
    args: --repo ${{ github.repository }} --cache-logs 25
    summary: "true"
    fail-on-findings: "false"

在参数中添加 --workflow ci.yml,即可将检查范围限定为一个工作流 — 当单体仓库的发布工作流会淹没 CI 信号时,这很有用。

固定 PR 评论 — 在拉取请求上发布发现的问题,并在每次推送时原地更新(需要 pull-requests: write):

permissions:
  contents: read
  pull-requests: write
steps:
  - uses: actions/checkout@v4
  - uses: linnea-bakshi/gha-doctor@v0
    with:
      pr-comment: "true"

每个 PR 只发布一条评论,每次运行时编辑,而不是重新发布。发现问题时会显示,问题修复后会变为“全部通过”;如果 PR 从一开始就没有问题,则不会发布评论。

仅检查 PR 引入的问题 — 添加 baseline: auto 即可隐藏既有问题:门禁(和评论)只涵盖此次更改新增的问题,就像用 git diff 检查 CI 规范一样。现有仓库无需先修复多年的历史问题,即可启用 lint 门禁:

- uses: actions/checkout@v4
- uses: linnea-bakshi/gha-doctor@v0
  with:
    baseline: auto        # PR base branch; fetched automatically

报告仍会统计隐藏的问题(“隐藏 3 个既有问题,修复 1 个”),因此改进也会体现出来。在 CLI 中使用:gha-doctor --baseline origin/main。

行内 PR 注释 — 默认启用。Action 会传入 --annotate,因此问题会以 ::warning 注释的形式显示在 PR 差异和运行日志中,无需设置代码扫描(受 GitHub 每种类型最多显示 10 条的限制;其余问题会汇总到一条通知中)。将 annotate: "false" 设为关闭。在 CLI 中,gha-doctor --annotate 会在报告之后输出相同的工作流命令。

代码扫描 — 将 --sarif 的发现结果作为注释显示在 Security 选项卡中:

- uses: actions/checkout@v4
- uses: linnea-bakshi/gha-doctor@v0
  with:
    args: --sarif > gha-doctor.sarif
    fail-on-findings: "false"
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: gha-doctor.sarif

可分享的报告 — --html 会将完整报告(发现的问题、运行历史、主要改进、健康评分)写入一个独立的 HTML 文件,无需外部资源或脚本 — 包含两个内联 SVG 图表:每次采样运行的耗时散点图(根据结果以绿色/红色显示),以及每个工作流的 p50→p95“典型日与糟糕日”范围条形图 (在线示例,针对 psf/requests 运行)。 将其作为构建产物发布,让团队中的任何人都能打开查看:

- uses: linnea-bakshi/gha-doctor@v0
  with:
    args: --html gha-doctor.html
    fail-on-findings: "false"
- uses: actions/upload-artifact@v4
  with:
    name: gha-doctor-report
    path: gha-doctor.html

输入项:args(默认 --lint-only)、version(默认:与 Action 标签匹配,否则使用最新版本)、github-token(默认:工作流令牌)、summary、 pr-comment、baseline、fail-on-findings、fail-on — 决定以何种严重级别触发退出码 2(any | warning | never,作为 --fail-on 传入;需要 v0.48.0+,旧版本固定项会跳过此参数并附带说明)— 以及 min-score — 健康评分低于阈值时失败(作为 --min-score 传入;需要 v0.61.0+;与 fail-on: "never" 组合即可仅按评分设置门禁)。二进制文件会保留在 PATH 中,供同一作业的后续步骤使用。固定 @v0 以使用最新的 0.x 版本,或使用 @v0.3.0 这样的精确标签 — 系统会自动安装对应版本的二进制文件。

完整的复制粘贴配置 — PR 门禁、每周健康报告、README 徽章、 SARIF/代码扫描、Grafana 指标、全组织服务群报告 — 请见 recipes 页面。其中的每个工作流都通过 gha-doctor 自身的检查(测试会强制验证)。

MCP 服务(让 AI Agent 运行医生)

gha-doctor --mcp 会作为 模型上下文协议 stdio 服务运行,因此 Claude Code、Cursor 和其他 MCP 客户端可以在对话中诊断 CI: “为什么这个仓库的 CI 很慢?”、“哪些测试不稳定?”、“gha-doctor 会在这里修复什么?”。完整指南(涵盖所有客户端、 工具参数、令牌设置和安全模型): MCP 服务文档。

# Claude Code
claude mcp add gha-doctor -- gha-doctor --mcp
// generic MCP client config
{
  "mcpServers": {
    "gha-doctor": {
      "command": "gha-doctor",
      "args": ["--mcp"]
    }
  }
}

七种工具,全部为只读——服务会报告并预览,但绝不会写入(应用修复仍需在 Shell 中显式运行 gha-doctor --fix):

工具功能
analyze_repo完整健康报告:Lint + 历史记录 + 不稳定/浪费/成本 + 评分 + 最佳改进项
lint_repo仅运行静态规则,适用于任何 GitHub 仓库或本地目录(离线)
preview_fixes精确展示 --fix 的差异,不会在任何地方应用
run_deep_dive深入分析一次运行:瀑布图、步骤回归、失败测试、日志尾部
pr_deep_dive深入分析一个 PR:最新提交的运行、得出结论所需时间,并深入分析其最近一次失败的运行——“为什么我的 PR 上的 CI 失败了?”
org_overview对组织中最繁忙的仓库进行全局分流
explain_rule规则 ID 的完整文档

该服务会继承你的环境:设置 GITHUB_TOKEN(或通过 gh 登录),以进行历史分析和日志读取;本地 Lint 可离线运行。 它支持当前 MCP 协议的两个版本(initialize 握手和无状态的 2026-07-28 版本)——已通过官方 MCP Inspector 验证。

它已列入官方 MCP 注册表,名称为 io.github.linnea-bakshi/gha-doctor, 因此支持注册表的客户端也可以直接从容器镜像运行,无需安装:docker run -i --rm ghcr.io/linnea-bakshi/gha-doctor:latest --mcp (进行历史分析时添加 -e GITHUB_TOKEN;本地目录 Lint 需要原生安装或挂载)。

静态规则

ID严重程度检查内容
D001warn由 PR 触发的工作流未设置 concurrency + cancel-in-progress(被取代的运行仍会持续消耗分钟数)——可自动修复
D002warn作业未设置 timeout-minutes(默认值为 360——一次挂起就会消耗 6 小时)——可自动修复
D003warnsetup-node / setup-python / setup-java 未设置内置的 cache: 输入——可自动修复
D004infocheckout 使用 fetch-depth: 0(克隆完整历史记录)
D005warncron 计划的运行频率高于每 15 分钟一次
D006info每次推送或计划运行都使用 macOS(计费倍率 10 倍)/ Windows(2 倍)运行器
D007warndocker/build-push-action 未设置 cache-from(每次运行都会重建所有层)
D008infoactions/cache 未设置 restore-keys(任何键未命中都会导致缓存完全冷启动)——可自动修复
D009info作业级别的 continue-on-error: true(失败被粉饰成成功)
D010info上传的制品使用默认的 90 天保留期限
D011warn每次触发时矩阵展开为 ≥20 个作业
D012info在 CI 中使用 npm install 而不是 npm ci——可自动修复
D013warn未限定范围的 push + pull_request 双重触发(每次 PR 提交都会运行两次 CI)
D014infocron 设在第 0 分钟(高峰负载时段;GitHub 会延迟/丢弃整点计划运行)——可自动修复
D015warnGitHub 已停止支持的操作版本(upload/download-artifact@v1–v3、cache@v1–v2)——该步骤每次运行时都会失败——可自动修复(仅适用于缓存;制品在 v4 中更改了语义)
D016warn已退役的托管运行器标签(ubuntu-20.04、windows-2019、macos-13 等)——作业无法运行;也会解析 ${{ matrix.os }}——可自动修复(仅限 ubuntu:同架构升级到 ubuntu-24.04;windows/macos 目标由你决定)
D017info没有任何机制更新你的操作固定版本——没有 dependabot github-actions 生态系统配置,也没有 renovate 配置(仓库级检查;仓库就是这样落入 D015/D016 的)
D018warnrun: 步骤中使用了已弃用的工作流命令——::set-env/::add-path(2020 年已禁用,运行时会报错)以及 ::set-output/::save-state(每次运行都会发出弃用警告,且已宣布将移除)——可自动修复(将简单的 echo 行改写为 $GITHUB_OUTPUT 风格的环境文件)
D019warn你发布的操作使用了已弃用的 Node 运行时——action.yml 声明 runs.using: node12/node16(运行器已移除这些运行时),或 node20(已宣布将于 2026 年秋季从运行器中移除:届时所有使用该操作的人都将无法使用)。扫描根目录/子目录/.github/actions 中的清单;复合操作的步骤也会检查 D015 和 D018
D020warn托管运行器标签已宣布退役——ubuntu-22.04/-arm(将于 2026 年 9 月 17 日起进行棕断,2027 年 4 月 17 日退役)以及 macos-14(自 2026 年 7 月 6 日起进行棕断,2026 年 11 月 2 日退役)。这是倒计时中的 D016:按你的计划迁移,而不是等到棕断期间——可自动修复(仅限 ubuntu)
D021info计划运行的工作流未设置 github.repository 保护条件——启用 Actions 的 fork 所有者也会运行你的 cron(查找密钥失败、在 fork 中产生机器人垃圾信息)

每条规则都附带一行修复建议,行号会指向 YAML 中的确切位置。完整参考文档——包括每条规则的检查内容、重要性和示例——见 docs/rules.md,也可离线运行 gha-doctor --explain D004 查看。

**抑制检查结果:**每条规则都是启发式判断,你的工作流可能是例外。可在标记行添加注释(或在其正上方单独一行),以忽略单条检查结果:

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # gha-doctor: ignore[D004]  (semantic-release needs history)

单独使用 # gha-doctor: ignore 会忽略该行上的所有规则;ID 不区分大小写。使用 --disable D004,D009 可在所有位置关闭相应规则。--fix 会遵循这两种设置——被抑制的检查结果绝不会自动修复。 自动修复: gha-doctor --fix 会通过精确的行编辑就地修复 D001–D003、D008、D012、D014 和 D015——你的注释和格式都会保留,不像 YAML 往返转换那样会被破坏。它会添加包含 cancel-in-progress: true 的 concurrency 块,将作业的时限设为 timeout-minutes: 30(之后可调整),读取锁文件,为 setup-node/python/java 选择合适的 cache: 值(pnpm-lock.yaml → pnpm、poetry.lock → poetry、pom.xml → maven,等等);当缓存键以 ${{ hashFiles(...) }} 结尾时推导出 restore-keys 前缀;将裸的 npm install 改写为 npm ci;将每小时第 0 分钟的 cron 移到稳定的哈希选定分钟(保持相同频率,避开 :00 高峰);并升级指向已停用版本的 actions/cache 固定版本。对于任何有歧义的情况——例如存在两个锁文件、YAML 使用流式格式、npm install <args>(npm ci 不接受软件包参数),或无法安全拆分的缓存键——都会跳过并附上说明,而不是猜测。D004(fetch-depth: 0)有意不自动修复:作业是否需要完整历史记录,只有你能判断。只有在结果能够解析且问题确实已消除时,才会写入任何内容。

先预览: gha-doctor --diff 会显示 --fix 将要应用的确切统一差异——在终端中带颜色,使用 --md 时以 ```diff 代码块呈现,使用 --json 时输出每个文件对应的字符串——且不会写入任何内容。它甚至可以处理尚未克隆的仓库:gha-doctor --repo psf/requests --diff 会获取工作流(以及锁文件列表,因此仍能检测 cache:),并打印补丁。 输出是标准统一差异:将其重定向到文件后,可在相同提交的工作副本上使用 git apply 干净地应用,例如: gha-doctor --repo apache/spark --diff > fixes.patch && git apply fixes.patch (已在 apache/spark 的 45 个工作流文件上验证:补丁可逐字节应用,且每个已修补文件仍是有效的 YAML)。

历史记录分析

获得 API 访问权限后,gha-doctor 会抽样检查你最近已完成的运行记录(默认 100 条)并报告:

  • 各工作流健康状况 — 成功率、p50/p95 持续时间、平均排队时间。
  • 不稳定的任务 — 在同一提交头上既失败又成功的任务 (通过重新运行或重复运行),并统计不稳定率和浪费的分钟数。
  • 最慢步骤 — 汇总所有运行,查看 p50 时间实际花在了哪里。
  • 矩阵均衡度 — 矩阵任务要等最慢的分片完成才会结束,因此拆分不均会直接增加 PR 反馈延迟(账单不会变,但等待时间会变)。统计包含 3 个或更多分片且至少有 5 次干净运行的组;报告会指出拖后腿的分片,以及每次运行等待它的中位分钟数。
  • 持续时间趋势 — 构建是否越来越慢?比较每个工作流在样本较早一半和较新一半中的 p50(仅成功运行)。只有成功次数达到 12 次且跨度超过 24 小时才会统计;只有变化同时超过 20% 和 1 分钟时才会报告;减速达到 30% 以上时,还会进入顶部收益中的“建议调查”项(诚实性门槛)。
  • 浪费 — 失败运行和重试所花费的分钟数,按运行器计费倍率加权(Linux 1x、Windows 2x、macOS 10x),并占所有采样运行的比例。
  • 僵尸定时任务 — 最近运行连续失败的计划工作流:无人关注却一再失败的定时任务。连续计划任务失败达到 5 次或更多、跨度达到 3 天或更久时报告,并估算其失败期间每月持续消耗的分钟数和费用。成功会结束连续失败;跳过或取消的运行既不会中断也不会延长连续失败。(上线当天的实际案例:一个顶级 Python 仓库中的每日维护定时任务,已连续失败 25 天以上。)
  • 被取代的 PR 运行 — 同一 PR 分支有更新推送时仍在运行、因而被取代的运行;细分为及时取消(并发控制已生效)与仍然跑完的运行,并统计替代推送到达后消耗的计费分钟数。这正是 concurrency + cancel-in-progress(D001、--fix)要避免的浪费,现在还会给出具体费用。仅统计 pull_request 事件(自动取消发布分支上的推送通常不妥);按头部仓库 + 分支分组,避免两个分叉仓库的同名分支被误判为相互取代;失败或重试的被取代运行仍计入上方的浪费项,不会重复计算。
  • PR 反馈时间 — 贡献者从推送到 PR 到最后一项检查完成所需的时间(中位数和 p95,包含排队时间),以及关键路径工作流:在大多数推送中最后完成的工作流,以及它在其他工作全部完成后额外耗费的中位时间——加快该工作流,实际能缩短的就是这段时间。仅统计已收到完整判定结果的推送:被取代、等待审批以及之后重新运行的推送均不计入(诚实性门槛)。
  • 成本估算 — 按 GitHub 公开的按量付费价格计算样本成本(Linux 每分钟 $0.008,Windows 2x,macOS 10x),并按 GitHub 的实际计费方式计量:每个任务的分钟数向上取整。取整产生的额外费用会单独报告——由 30 秒任务组成的矩阵会悄悄让自己的账单翻倍。自托管任务不计入(GitHub 不会对其收费)。使用标准运行器的公共仓库免费;此时估算结果表示“若在私有仓库中运行,这会花费多少”。
  • 缓存检查 — 检查缓存使用量相对于每个仓库 10 GB 限额的情况(超过限额后,GitHub 会优先清除最旧的缓存,导致构建失去热缓存),连续 7 天或更久未使用的过期缓存,以及固定在 refs/pull/* 上的兆字节数——PR 缓存无法被其他任何分支访问,因此合并后就成了纯粹的死重,占用本可留给有效缓存的空间。
  • 制品检查 — 查看是谁上传了存储占用,以及制品保留了多久。私有仓库的制品费用为每 GB 每天 $0.008,默认保留 90 天,因此每次运行上传的大型制品会悄悄累积成庞大的稳定存储占用(上传速率 × 保留时间)。报告会按名称列出最近上传的制品及其上传者,标记仍使用 90 天默认保留期的大型上传者(可配合规则 D010),并预测稳定状态下的 GB 数和每月费用——只有样本跨度足够长、能诚实反映上传速率时才会报告。
  • 顶部收益 — 报告最后会给出按优先级排序的待办清单:列出少数值得落实的改动;样本数据足够时会量化费用(“减少失败和重试 — 约 $28/月”、“合并小任务 — 约 $22/月”、“停止重复运行 PR 推送”),每项都会指向对应规则,或在 gha-doctor 能自行应用改动时指向 --fix。只有运行样本跨度达到 ≥3 天时才会预测月度数据;低于该时长时,只提供真实的样本总量,并说明原因。
  • 按名称列出不稳定测试(--flaky-logs N)— 不稳定任务检测会告诉你问题出现在哪里;此项会告诉你是哪个测试。它会读取最多 N 个失败任务运行的日志(其提交也曾通过,即不稳定任务表中的同 SHA 失败+通过配对),并利用各测试框架自身的失败摘要提取失败测试——31 个测试框架系列: pytest、Python unittest(包括 Django 的运行器)、go test、cargo test、 jest、vitest、playwright、Cypress、mocha、WebdriverIO、ava、TAP(bats、node-tap、tape、 node --test)、rspec、minitest、phpunit、exunit、 maven surefire、gradle(JUnit)、sbt、.NET(xunit v3 / VSTest)、XCTest (xcodebuild、swift test 和 xcbeautify 输出)、swift-testing、dart/flutter test、LLVM lit、meson test、GoogleTest、CTest、doctest、bazel、cargo-nextest 和 node-core test.py ——也包括在 docker build 内运行的测试(会移除 BuildKit 的日志前缀)。输出按每个测试在多少份采样日志中失败排序,同时列出不同提交和任务;排名第一的问题测试也会列入顶部收益(psf/requests 的实际案例: tests/test_requests.py::TestRequests::test_pyopenssl_redirect)。无法识别的失败会如实说明,而不会猜测——构建错误并不等于不稳定测试。如果不稳定运行的日志没有可识别的格式,则会将其上传的**测试报告制品(JUnit XML / .NET TRX / NUnit3 / TestNG)**作为后备来源(仅当该次运行中的每个失败任务都已确认不稳定时才会使用——真正损坏的同级任务不得被误判为不稳定;按运行归因,另设小节)。需要身份验证,与所有需要下载日志的功能相同。
  • 缓存命中率(--cache-logs N)— API 不会告诉你缓存是否真的命中;只有日志文本中记录了这些信息。此功能会采样 N 份最近任务日志(每份日志各发起一次 API 请求,并轮流覆盖不同任务名称,避免频繁输出日志的矩阵挤占其他任务的份额),并解析 actions/cache、setup-go、setup-node 等工具输出的缓存标记:精确命中、通过 restore-keys 部分命中、未命中、下载的兆字节数——按键模式分组,并折叠哈希值(Linux-go-4ae0e4f8… → Linux-go-*)。此外还会统计因“unable to reserve cache”竞争而未能保存的缓存——并发任务悄悄重新构建相同的键。需要身份验证:即使是公共仓库,没有令牌时下载日志也会返回 403。

单次运行深度分析(--run)

“为什么这次运行这么慢?”——将 --run 指向运行 ID、粘贴的运行 URL 或 latest,即可深入剖析这次运行:

  • 运行整体耗时瀑布图:展示运行的挂钟时间:排队等待(·)与执行 (█),按结论着色,让并行空档和 Runner 资源不足一目了然。
  • 步骤耗时与工作流自身的历史记录对比——每个作业和步骤都会与同一工作流最近 8 次成功运行的中位数进行比较。结论会指出性能回退项:⚠ "Install dependencies" in build (3.12, windows-latest): +59s vs its p50 (3.7x slower)。
  • 失败的运行会首先说明失败位置(✗ job "lint" failed at step "golangci-lint")——绝不会因为“完成得很快”而受到称赞。
  • 直接在报告中显示失败步骤的日志末尾(已认证的运行):显示失败步骤的最后 20 行——根据步骤自身的时间戳从作业日志中截取,以 ##[error] 标记为锚点,并删去清理相关的日志——这样无需点击进入 Actions UI,就能看到实际的编译器错误或 --- FAIL 行。使用 --log-tail N 调整(设为 0 可关闭)。
  • 按名称列出失败测试(已认证的运行):作业日志会通过同一组为 --flaky-logs 提供支持的31 种框架提取器 进行处理,因此失败运行的结论会显示 ✗ job "test (3.12)" failed at step "pytest" — 3 failing tests incl. tests/test_retry.py::test_backoff,而无需你在日志中滚动查找。 按设计,构建和基础设施失败不会提取任何内容——没有可识别的测试失败输出,就不会列出测试名称,也不会猜测。
  • 测试报告制品作为后备方案(JUnit XML / TRX / NUnit3 / TestNG)(已认证的运行): 如果失败作业的控制台输出中没有可识别的格式,系统会检查该运行上传的制品,查找 JUnit XML 报告(pytest --junitxml、surefire、Gradle、jest-junit、ctest --output-junit 等)、 .NET TRX 文件(dotnet test --logger trx)、NUnit3 报告 (nunit3-console、dotnet test --logger nunit、Unity 的测试运行器)或 TestNG 原生结果(testng-results.xml——Selenium 风格的自动化测试套件常常只上传这一文件),并从中列出失败测试名称——适用于任何会写入行业标准报告文件的框架,且名称准确无误。结果会在运行级别列出,并注明来源制品,因为制品属于运行,而非某个作业——绝不会错误归因。
  • **能够识别重新运行情况:**尝试编号、重新运行的作业与沿用先前尝试结果的作业,以及明确说明的计费影响。
  • 遵循常规的可信度规则:可比较的成功运行少于 3 次时,会附注并取消比较,而不会伪造比较结果。进行中的运行会显示“截至目前”,而不会给出结论。

也支持 --json 和 --md(gha-doctor --repo owner/name --run latest --md 的输出可直接粘贴到故障事件议题中)。

PR 深度分析(--pr)

“我的 PR 上的 CI 为什么失败了?”——执行相同的深度分析,但从 PR 编号(或 #N,或粘贴的 PR URL)开始——URL 还会固定仓库:

gha-doctor --repo owner/name --pr 123

它会获取 PR 头部提交上的每次工作流运行,显示各自的结果、耗时和尝试次数,然后:

  • 反馈时间——提交等待明确 CI 信号的时长:直到首次失败(失败一出现就可采取行动)的时间,或所有检查通过时直到全部变绿的时间。正在进行的运行会标记为进行中,不会判定结果。
  • 重新运行迹象——尝试次数大于 1 的运行意味着有人点击了重新运行;如果这样一来结果由红变绿,你很可能遇到了不稳定检查。
  • 深入分析最新一次失败运行的完整过程——瀑布图、步骤回归、明确列出的失败测试、失败步骤日志末尾内容——与 --run 展示的内容完全相同,无需先查找运行 ID。
  • 一如既往,诚实呈现:包含多个提交的 PR 只分析头部提交;没有运行记录的 PR 会明确说明,而不是呈现空报告。

健康评分与徽章

所有测量结果会汇总为 0–100 分的 CI 健康评分(A+–F),并逐项列出,让你清楚了解每项扣分情况:

Health score
  F  (54/100, run history only)
  ✗ success rate       −17.5  72% of 100 sampled runs succeeded
  ! queue time         −0.3   average 6 s waiting for a runner
  ! flakiness          −5     1 job failed AND passed on the same commit
  ! wasted minutes     −4.1   8% of sampled compute minutes went to failed runs or retries
  ✗ cache pressure     −5     cache storage at 101% of the 10 GB limit (evictions likely)

--badge health.svg 会生成一个 shields 风格的 SVG 评分图,可放入你的 README;一个小型定时工作流还能让它保持最新。 --score-history scores.jsonl 会将每次运行记录到可提交的 JSONL 文件中,并打印与上次相比的变化(Δ +7 since 2026-07-22 (B 84 → A 91)),包括哪些组成项有所改善或退步。两个标志一起使用时,徽章会显示近期评分的迷你趋势图——一眼就能看到趋势,并与构建徽章并列。权重、公式、趋势跟踪和徽章工作流详见 docs/score.md。

Prometheus / Grafana(--prom)

gha-doctor --prom ci-health.prom      # alongside the normal report

--prom 会以 Prometheus 文本展示格式写入所有测量得到的汇总数据——健康评分、按严重程度划分的问题、各工作流的成功率和 p50/p95 耗时、排队时间、浪费和向上取整后的计算资源消耗(秒数和美元)、不稳定任务、僵尸定时任务、相对于 10 GB 限制的缓存大小、被取代运行造成的浪费、PR 反馈时间。按计划运行后,CI 健康状况就能成为 Grafana 仪表板的一部分,展示真实历史数据,而非某个时间点的报告。文档中附带可直接使用的仪表板(健康评分仪表盘、浪费量和耗时趋势、与 10 GB 限制对比的缓存、按仓库筛选的模板变量)——详见 docs/grafana.md,该仪表板已通过实时 Grafana + Prometheus 堆栈验证。

两种简单的接入方式:

  • Textfile collector(自托管运行器或任何装有 node_exporter 的机器):将文件写入收集器目录—— gha-doctor --prom /var/lib/node_exporter/textfile/gha-doctor.prom。

  • Pushgateway(托管运行器):由定时工作流推送导出数据——

    - run: |
        gha-doctor --fail-on never --prom metrics.prom
        curl --data-binary @metrics.prom \
          https://pushgateway.example.com/metrics/job/gha-doctor/instance/${{ github.repository_owner }}-${{ github.event.repository.name }}
    

同样坚持如实呈现:运行未测量的内容完全不会生成任何时间序列(仪表板上的空缺才是真相;填充为零的时间序列则是谎言),而测量得到的零值——例如采样时间段内没有不稳定任务——则是真正的 0。每个值都是描述采样时间段的仪表;gha_doctor_sample_since_timestamp_seconds 表示该时间段回溯到何时,而 gha_doctor_last_run_timestamp_seconds 则用于检测数据是否过时。

全组织分诊(--org)

gha-doctor --org yourorg              # 20 most recently pushed repos, 100 runs each
gha-doctor --org yourorg --max-repos 50 --md   # bigger fleet, Markdown for an issue

在一个屏幕上查看整个组织:按仓库统计运行量、失败率、p50/p95 时长,以及每 30 天的预估墙钟运行分钟数,并按消耗最多的仓库排序。用户账号也适用,并会自动跳过分叉仓库和已归档仓库。它还会指出整个集群中的僵尸定时任务——其采样运行结果连续失败的计划工作流(检查条件与仓库级检查相同),直接从已获取的运行样本中识别,不会产生额外 API 成本。失效的 lock-issues 机器人和损坏的夜间构建会在数月间悄悄抬高仓库失败率;集群视图会直接指出它们。这里不会预估每月 $ 成本——组织扫描不会获取作业数据,因此价格只能靠猜;使用 --repo 深入查看可计费的消耗。

这是刻意设计成低成本的:每个仓库只需一次 API 请求(仅运行级数据),因此拥有 50 个仓库的组织只需约 51 次请求,而不是数千次。这也意味着显示的分钟数是每次运行的墙钟时间,而非可计费的作业分钟数——并行作业会分别全额计费——所以请将其视为分诊视图:找出最突出的仓库,然后使用 --repo org/name 深入查看精确的逐作业计费、易出错作业和缓存健康状况。

集群卡片(--org + --svg)
gha-doctor --org yourorg --svg fleet.svg

将集群表格写入独立的 SVG 卡片,可嵌入组织个人资料 README 或仪表板中(最繁忙的 12 个仓库 + 汇总其余仓库;可像评分徽章那样通过定时工作流重新生成):

cli 组织的集群卡片

对比

actionlintzizmorgha-doctor
侧重点正确性安全性速度 / 成本 / 可靠性
静态工作流检查✅✅✅(性能和成本规则)
使用你的运行历史记录❌❌✅
易出错作业检测❌❌✅
浪费分钟数估算❌❌✅
$ 成本估算(含进位)❌❌✅
缓存限制 / 陈旧缓存检查❌❌✅
缓存命中率测量(基于日志)❌❌✅
构件存储 / 保留期限检查❌❌✅

它们可以组合使用:三个工具都运行。有关详细且客观的分析——包括这三个工具的重叠之处——请参阅 gha-doctor 与 actionlint、zizmor 的对比。

许可证

MIT © Linnea Bakshi

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。