mcpField
I
investigating-ci-failures

investigating-ci-failures

github.com/posthog测试与质量95148 次安装今天更新
代码仓库

关于 investigating-ci-failures

这项技能提供基于 CI 日志与作业历史调查测试失败的方法,帮助开发者判断故障属于自身 PR、主干变更还是偶发问题,并定位相关提交。它强调用现有数据完成归因,无需重新运行 CI。

主要功能

  1. 01按指纹归类 pytest 失败
  2. 02区分 PR 故障、主干故障与偶发失败
  3. 03通过作业历史定位提交边界
  4. 04核对修复提交与责任 PR
  5. 05说明数据延迟和覆盖范围限制

适用场景

  1. 01调查单个失败测试的原因
  2. 02确认主干何时变红及修复时间
  3. 03判断多个分支上的失败是否源于主干
  4. 04核实失败是否具有偶发特征

MCP 相关

使用 engineering-analytics-broken-tests 进行整体故障分类,并按需调用失败日志、偶发测试等 MCP 工具。调查还会查询 engineering_analytics_ci_failures 和 engineering_analytics_ci_job_history 两个数据仓库视图。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/posthog/ai-plugin --skill investigating-ci-failures

安装后在 Claude Code 中输入/investigating-ci-failures确认已加载。

附带文件references/investigation-queries.md

详细介绍

调查 CI 失败

任务:针对一个失败的测试或一次红灯运行,得出开发者可以据此采取行动的结论—— 你的代码导致 / 源自主干 / 不稳定。如果源自主干,还要找出问题提交 SHA、作者、PR,以及修复是否已经合入。以下所有推导都基于已有数据;无需重新运行 CI 即可得出结论。

两个数仓视图是分析基础(均为非物化视图——数据始终最新,可随时查询):

  • engineering_analytics_ci_failures — CI 日志中每行 pytest FAILED <nodeid> 对应一行, 已预先生成指纹(fingerprint = 测试 ID + 数字/十六进制归一化后的错误)。按 fingerprint 分组,即可获取首次/最近出现时间、出现次数和分支分布。
  • engineering_analytics_ci_job_history — 每次作业尝试对应一行,包含 conclusion 和提交 归属信息:head_sha、commit_author_name、commit_message、commit_pr_number(生成该提交的已合并 PR,这是 master 推送运行唯一的 PR 归属信息)。绿色运行记录在此;日志中只有失败记录,因此所有“何时变红 / 再次转绿”的问题都必须从这里查询,而不是从日志查询。

每个步骤的可直接复制 SQL 都在 references/investigation-queries.md 中。

先看全局:当前有哪些问题

如果想知道“现在有哪些 CI 失败值得关注”(还没有特定测试目标时),engineering-analytics-broken-tests MCP 工具会对所有仍在发生的失败一次性进行以下分类:按指纹汇总最近 2 天的失败,并为每项标注 breaking_master / blocking_merge_queue / novel_burst / potentially_resolved / flaky / pr_only,按紧急程度排序,同时提供 breaking_master_jobs(默认分支最近一次运行结果为红灯的作业)。将它用作分诊入口,然后进入下方的单个失败调查流程,查明问题来源。它是手动生成指纹流程的自动化对应方案;要将特定失败定位到某个边界和作者,仍需使用手动查询。

blocking_merge_queue 是下方手动表格未涵盖的唯一类别,因为它看起来像单分支失败,实则不然。合并队列会在门禁分支(trunk-merge/pr-<n>/…)上运行完整测试套件;该分支包含 master、该 PR,以及队列中所有与其影响目标有重叠的前置 PR(实际上通常是队列中的大多数 PR)。因此,此处的失败发生在一个已通过该 PR 自身 CI 的提交上:原因可能是与期间合入或排队的改动冲突,也可能是队列中其他 PR 自身的问题,默认不应归咎于该 PR 的缺陷。应将它理解为“此问题阻止了合并”,列出门禁分支运行过的内容(查询 8),并与 trunk 对比,而不是只看该 PR。

四种故障形态

先为故障建立指纹(参考资料中的查询 1),然后读取其形态——看三列即可分类:

形态解读后续步骤
1 个分支,任意时间窗口该 PR 自身的问题查看其故障日志;完成
1 个 trunk-merge/pr-<n>/… 门禁分支队列中的其他 PR,或问题已修复并入库执行查询 8,然后比较门禁分支与 trunk
多个分支,短时间内密集出现,命中 mastertrunk 故障(master 当前或曾经标红)边界查询 → 找出根因(如下)
多个分支,数天或数周内零星出现间歇性故障使用 engineering-analytics-flaky-tests 进行佐证

为何跨分支意味着 trunk 有问题:PR CI 会运行与 master 合并后的 PR,因此,master 上的一个坏提交会导致所有同时运行的 PR 失败。在短时间窗口内,多个互不相关的分支都出现故障,这是 master 合并故障的特征,而不是这些 PR 的代码有问题。如果提问者的 PR 没有责任,请明确告知——这通常是回答中最有价值的一句话。

trunk 故障 → 找出根因

运行边界查询(查询 2):查看仅包含 master 的故障作业历史,并按 created_at 排序。模式一目了然:

... success success | failure failure ... failure | success ...
                    ^ first red = the culprit row  ^ first green = the fix row

根因所在的记录包含全部信息:head_sha、commit_author_name、commit_message(其中说明了变更内容)和 commit_pr_number。首次通过的记录以相同方式标出修复。指明责任人之前,先检查可信度:根因提交是否有可能影响故障区域(根据其消息/PR 差异与故障测试所在模块进行判断)?如果边界落在无关提交上,说明可能是分片或时序噪声——扩大时间窗口,并检查相邻提交后再下结论。

然后验证 ci_failures 中的故障时间窗口是否吻合(first_seen 应在根因合并后不久,last_seen 应在修复后不久,随着 PR 队列排空而结束)。若不吻合,说明你看到的是两个恰好共用同一测试的问题。

间歇性故障 → 佐证,不要猜测

称其为“间歇性故障”之前,先查看同一分支的其他尝试。 单次运行无法区分间歇性故障与确定性故障,而 ci_job_history 已包含该分支每次尝试的 conclusion。如果某个分支上的每次运行都标红,就不存在可供查找的间歇性故障;建议重跑只会浪费一次运行,因为重试不会改变任何输入。

仅凭零星出现的形态只能作为线索,不能作为证据。engineering-analytics-flaky-tests MCP 工具会读取按测试划分的 CI 时间跨度数据(重跑后通过信号——某项测试在同一作业中失败后又在重试时通过),在有覆盖的情况下,这是更有力的信号。只看次数,不看比率:低于数据生成器时长阈值的通过运行不会被记录,因此没有可靠的分母。

每次回答都必须说明的注意事项

  • 日志中只有失败记录。 ci_failures 中没有绿色基线;缺少指纹只是弱证据(任务可能根本没有运行)。绿色记录仅来自 ci_job_history。
  • 指纹仅适用于 pytest(v1)。 Jest / playwright / cargo 的失败会出现在原始失败日志中,但不会记录在 ci_failures 中。对于这些失败,请通过 MCP 工具 engineering-analytics-ci-failure-logs(按 PR 查询)/ engineering-analytics-run-failure-logs(按运行查询)查看原始失败日志。 获取某次运行时,请携带 ci_engine 和 run_id,或携带 latest_ci_engine 和 latest_run_id。 引擎为 null 表示历史来源未知;请勿按整数 ID 将其与已知引擎关联。
  • 测试运行前就失败的任务,在所有测试级数据面中都不可见。 没有 FAILED 行,就没有指纹、没有 span,也没有内容可供 flaky-tests 工具读取——失败确实发生了,但测试级查询应保持沉默,而不是说“没问题”。设置、docker 和 runner 失败只会显示为任务结论(查询 7);这也是此处唯一能得出可信失败率的数据面,因为它记录了成功记录。
  • 归咎于缓存前,先检查 runs-on。 在 depot-* runner 上运行的任务会通过 Depot Cache 解析 actions/cache,而 GitHub Actions 缓存 API 无法查看 Depot Cache,因此此处结果为空并不代表缓存已被清除。请查看任务自身的 Cache not found for input keys: 行,确认实际请求的键。无论哪种情况,两个步骤都会显示为成功:缓存未命中时,actions/cache/restore 仍会成功;actions/cache/save 失败只会产生警告,因此成功的生产者任务可能什么也没存下来。
  • 不同数据源的新鲜度各不相同。 日志近乎实时地流入;数仓中的 jobs/runs 表通过 webhook 同步,可能会延迟。处理正在发生的事故时,请从 ci_failures 开始,并在信任边界前检查数仓的 max(created_at)(查询 5)。基于过时数仓计算出的边界会指向错误的提交。
  • 运行的 conclusion 可能过时,直到 workflow_run webhook 将其更新为止(SPEC §7)——请谨慎看待非常近期且“无失败”的尾部记录。
  • 重试: run_attempt > 1 的行表示同一任务重新运行。失败在第 2 次尝试时消失,是不稳定信号;持续失败到第 5 次或之后,则是确定性失败。
  • 回退: 回退会显示为一个新的首个成功(或首个失败)提交,其 commit_pr_number 是执行回退的 PR——归因跟随回退,而非原始提交。
  • 所有日志查询都必须限定时间范围。 失败日志流规模很大;不设范围的扫描会触及读取上限。14 天的范围几乎足以覆盖所有调查。
  • 也要查询对应的数仓数据。 若 ci_job_history 查询仅按 created_at 确定时间窗口,就会强制扫描全部 jobs——解析后的时间戳是计算列,parquet 扫描无法据此剪枝。除了精确的 created_at 条件外,还要加上较窗口起始日早一天的粗略字符串下限 created_at_raw >= '<YYYY-MM-DD>',以跳过无关数据;created_at 仍是精确筛选条件。

选择使用场景

问题使用
“当前 CI 中哪些问题尚未解决?”engineering-analytics-broken-tests MCP 工具(已分诊、分类)
问题相同,但想从终端查看hogli ci:insights — 这些端点,范围限定为当前检出仓库
“我的 PR 的 CI 为什么失败?”engineering-analytics-ci-failure-logs MCP 工具(限定 PR、分组)
“是谁弄坏了 master / X 是什么时候开始的?”使用上述两个视图和工作流
“X 是否不稳定?”根据 ci_failures 和 flaky-tests 工具梳理情况
“这种设置/基础设施故障常见吗?”作业结论,查询 7(不会为此类问题生成测试行)
“master 当前有哪些失败?”broken-tests 工具中的 breaking_master 行和 breaking_master_jobs
“CI 是否缓慢/成本高/PR 卡住了?”diagnosing-ci-and-merge-bottlenecks 技能
“将其保存为仪表板/洞察”turning-engineering-analytics-into-insights 技能

输出预期

先用直白的话给出结论,并说明责任归属或排除嫌疑(“不是你的 PR——master 在 UTC 时间 08:01 到 09:58 期间因 #68727 而故障;由 #68855 修复”),然后给出证据:边界行、指纹时间窗口、出现次数/分支计数。客观指出作者(他们编写了问题提交),不要指责——提交说明和 PR 链接留给读者自行判断;而且很多时候,“问题提交”只是一次合理的更改,却依赖了未模拟的测试依赖项。

评论

支持 Markdown