mcpField
D
debugging-ci-failures

debugging-ci-failures

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

关于 debugging-ci-failures

用于排查 PostHog CI 失败的工程技能,结合聚合洞察与单次运行检查来分类故障、定位日志,并将已确认的易波动测试转交相关技能。

主要功能

  1. 01聚合 CI 失败摘要
  2. 02失败分类指导
  3. 03只读运行检查
  4. 04日志提取流程
  5. 05专门技能转交

适用场景

  1. 01排查多个 CI 运行中的失败
  2. 02检查特定 GitHub Actions 运行
  3. 03判断失败是广泛发生还是仅限分支

MCP 相关

该技能会使用 PostHog MCP 工具获取失败作业日志和结构化测试失败详情;测试波动排查也会转交给 trunk MCP 服务上的工具。

安装

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

$npx skills add https://github.com/posthog/posthog --skill debugging-ci-failures

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

附带文件references/master-red-incident.mdreferences/master-red-workflow-setup.md

详细介绍

调试 PostHog CI 失败

在你提出对 CI 的修改之前,先查看 已经尝试过的事情 中是否已有这个想法。其中记录了测量了什么,以及为什么一些听起来不错的改动被回滚或拒绝。

找到第一个有意义的失败,对其进行分类,在合适时在本地复现最小的有用案例,并报告结果。除非用户明确要求,否则避免公开可见或不可逆的操作。

对于交互式调查,从 hogli ci:insights 摘要开始。它跨运行和分支进行聚合,这是 gh 无法低成本做到的,并告诉你失败可能是主干导致的、仅限门禁的,还是局限于少量分支。gh 对于单次运行的当前状态和归因是权威的。用摘要来决定_要检查什么_;用 gh 来确认这是_谁的_失败,以及在给定运行中到底什么失败了。

无人值守的 Master-red 工作流是例外。 遵循 其参考,其中定义了在任务沙箱中可行的第一步。

此技能负责分诊和分类。一旦确认失败是偶发的,就移交给 fixing-flaky-tests 技能,它负责本地复现、根因修复和 N 次运行验证。对于“谁破坏了 master”——罪魁祸首提交和修复它的提交——移交给 investigating-ci-failures 技能,它负责绿/红边界分析。对于聚合流水线健康(CI 是否变慢、哪个工作流是长杆、PR 合并需要多长时间),阅读 diagnosing-ci-and-merge-bottlenecks。两者都是 products/engineering_analytics/skills/ 下的产品技能,此处不可调用:阅读该路径下的 SKILL.md。

首先排除平台故障

GitHub Actions 宕机的频率足够高,应该在阅读任何日志之前先检查,而平台事件会让其他所有信号都成为症状。它只需要加载两个页面: https://www.githubstatus.com/ 用于 GitHub,https://status.depot.dev/ 用于 runner。每当失败范围广泛时——多个工作流同时失败、一批运行一起失败、作业在 Checkout 之前就死掉,或任何跨不相关 PR 的红色——都要检查它们。

将宕机报告为宕机,指出组件名称,并停止建议重新运行。

安全规则

未经当前对话中的明确批准,不要执行以下任何操作:

  • 重新运行或取消 GitHub Actions 运行,或重试、重新运行或取消 Depot CI 运行。
  • 通过任何 CLI、MCP 或 API 工具发布 GitHub 评论、PR 审查或 issue 评论。
  • 推送提交、强制推送、重命名分支或删除分支。
  • 编辑 .github/workflows/ 文件(CI 基础设施更改需要人工审查)。
  • 合并、关闭或重新打开 PR。
  • 接受或更新快照。

只读的 gh 调用和只读的 GitHub 工具是可以的。如果你需要更改本地 Git 状态,请确保它对任务必要且不会覆盖无关工作。

工作流

1. 从 CI 洞察开始进行交互式调查

hogli ci:insights 读取 PostHog 自身的工程分析数据——单次运行无法展示的跨运行失败历史。在进行任何原始的 gh 日志考古之前,先查阅它。

hogli ci:insights                                # digest for the current repo + branch
hogli ci:insights search "<error or test name>"  # match a specific failure
hogli ci:insights view <ref>                     # one failure in full
hogli ci:insights view <ref> --logs              # ...plus the failing log lines
  • 宽泛问题(“CI 是不是红了?”、“今天 master 是绿的吗?”、“现在什么坏了?”):无参数摘要会直接给出答案。它给出默认分支的判定(有多少工作流在最新运行中失败,以及是哪些),按状态分组的实时失败,当前在主干上标红的作业,默认分支失败的分组信息流,以及你的分支所属的 PR。你通常根本不需要指定目标 PR 或运行;直接从摘要中报告。
  • 具体失败:在阅读日志之前,先运行 search "<error>" 来匹配它,然后对摘要或搜索打印出的某一行运行 view <ref>。--logs 会打印该失败最新运行中精简后的失败行,通常足以在不碰 gh 的情况下进行分类。

将每一行的 state 读作分诊排名:

状态含义
breaking_master在默认分支上失败,且该作业的最新运行仍然是红的
blocking_merge_queue在窗口期内仅在合并队列门控分支上失败
novel_burst一天内新出现,已在分支间扩散,尚未进入主干
potentially_resolved触及主干,但该作业的最新运行已重新变绿
flaky在超过一天的时间里在两个或多个分支上零星出现
pr_only分支扩散有限;作业状态可能缺失或落后于失败行

potentially_resolved 是提示,不是结论:在将失败报告为已修复之前,先从运行数据中确认。

blocking_merge_queue 证明窗口期内发生过门控失败,并不证明它仍在阻止合并。在将其报告为活跃状态之前,用 gh 检查当前的队列运行。同样地,在将失败归因于某个 PR 之前,从当前运行中确认 pr_only;当作业状态缺失或过时时,它也是回退选项。

无论你报告什么,都要带上这些注意事项:

  • 失败分组仅适用于 pytest。Jest、Playwright 和 cargo 的失败只出现在摘要的分组 master 失败部分,绝不会作为带有 ref 的行出现。
  • 每个计数都是绝对数,绝不是比率。通过的运行不在测试级数据中,因此没有可供引用的分母。作业结论确实会记录绿色通过;下面的基准率部分说明了如何从中获得真实比率。
  • 运行的结论可能会滞后,直到 GitHub 的 workflow_run webhook 确定它,因此在实时事件期间,用 gh 确认具体的运行。

如果此机器上没有人登录,hogli ci:insights 会以 78 退出。将退出码 78 视为“没有可用的 CI 洞察”,并回退到下面基于 gh 的检查——然后告诉用户可以运行一次 hogli posthog:login,它会打开浏览器且不需要 API 密钥。不要自己运行它:它会等待一个你看不到的同意屏幕。按照安全规则呈现你的发现——不要自动应用修复。

2. 找到失败的运行(针对特定失败)

按以下顺序确定目标:

  1. 如果用户给出了 PR 编号、run ID、检查名称或分支,就直接使用它。
  2. 否则,用 gh pr view --json number,headRefName,statusCheckRollup 从当前分支推断。
  3. 如果两者都不行,向用户询问 PR URL 或 run ID。不要猜测。

被从合并队列中踢出的 PR 是例外:它自身的检查是错误的目标。 Trunk 会在一个 trunk-merge/pr-<n>/<uuid> 分支上测试每个排队的 PR,该分支包含 master 以及所有排在它前面、且受影响目标与其自身重叠的 PR。lane 脚本会故意多报目标,因此实际上这几乎是队列中的大部分。所以:

  • 失败的运行在该分支上,从不在 PR 的 head SHA 上。要从 trunk merge status <n>(/merging-prs 第 4 步)获取,而不是 gh pr checks。Trunk 在这里不发布任何检查运行。 该分支是临时的;运行及其日志保留在 GitHub 上,仓库将其作业保存在那个 head_branch 下(investigating-ci-failures 参考资料中的查询 8)。 当 CI_BACKEND_DEPOT_MERGE_QUEUE_PERCENT 将批次交给 Depot CI 时,后端 CI 是例外:后端测试随后在 Depot 上运行,Django Tests Pass 检查会链接到该运行。
  • PR 自身的检查可能是绿色的,而失败作业被跳过或被收窄。在 PR 上,路径过滤器只看到该 diff,Django 测试套件运行一个选定的子集;在队列分支上,diff 是每个携带的 PR 的 diff,完整矩阵总是运行。一个仅文档的 PR 可能被它自己的 CI 从未运行过的作业踢出。
  • 该分支指名一个 PR,但携带多个。在找到导致失败的变化之前,它上面的失败并不能作为针对该 PR 的证据;该分支的其他合并提交是首要嫌疑对象。
  • …-bisection 分支是"其他合并提交是首要嫌疑对象"的例外:Trunk 在一批失败后创建它,它只携带 master 加上那一个 PR。测试 PR 的正文会指出它所基于的 master SHA;如果 master 在该 SHA 上的运行通过了同一个测试,那么失败是依赖环境的竞态(fixing-flaky-tests 第 1 步),而不是这个 PR 的。
  • 在摘要中,这就是 blocking_merge_queue 状态。
3. 读取 CI 报告评论(针对 PR)

在读取日志之前,先读取共享的 CI 报告评论。它在多个区块中收集独立的 CI 信号和提示。

读取完整的原始评论,而不是 GitHub 折叠后的视图。即使 PR 有很多评论,以下命令也能找到它:

gh api --paginate "repos/<owner>/<repo>/issues/<pr>/comments?per_page=100" \
  --jq '.[] | select(.user.login == "github-actions[bot]" and (.body | startswith(""))) | .body'

检查每个 ci-report:section 区块,包括未知区块。记录其标题、状态、摘要和链接。不要停在第一个 fail 区块,也不要把评论标题当作总体结论。

  • 把 fail 当作线索,并将其与当前任务对应起来。
  • 把 alert 和 warn 当作非阻塞性发现。查看其详情并遵循其说明的操作,但除非对应任务失败,否则不要称任一为失败任务。
  • ok 和 info 不是测试结果。
  • 报告缺失意味着报告器没有运行或无法写入。这并不能证明 PR 是健康的。

报告只是摘要。没有 head SHA 或运行链接的区块可能已过时。请对照当前任务进行确认。如果它与当前日志不一致,请报告该不一致,并以日志作为原因依据。

只读检查:

gh pr checks <pr>
gh pr view <pr> --json statusCheckRollup
gh run view <run-id> --json jobs,conclusion,name,workflowName,url
gh run view <run-id> --log-failed

仅当 --log-failed 缺少失败命令或足够的周边输出时,才使用完整任务日志:

gh run view <run-id> --log --job <job-id>

给定运行 id,engineering-analytics-run-failure-logs MCP 工具会返回每个失败任务的错误区域及原始行号,且已精简。一次调用即可替代任务列表加日志下载,并且当任务在任何测试运行前就终止时也能正常工作。它受 Logs 保留期限限制,因此对于更早的运行请回退到 gh。如果给定的是 PR 编号而非运行 id,engineering-analytics-ci-failure-logs 会在该 PR 推送过的每次运行中执行相同操作,因此更早推送的失败仍然存在。

在分类前提取以下内容:

  • 工作流名称或文件,例如 .github/workflows/ci-backend.yml。
  • 任务名称,例如 backend-tests (4/10)。
  • 步骤名称,例如 Run pytest。
  • 失败命令以及最小可用的输出摘录。

扫描日志时,搜索 FAIL、Error、error:、assert、Traceback、exit code 和 ##[error]。在第一个能解释本次运行结论的失败步骤处停止。摘录保持在 40 行以内。

对于测试任务失败,trunk MCP 服务的 investigate-ci-failure 工具是跳过日志扫描的捷径:给它运行 URL(https://github.com/PostHog/posthog/actions/runs/<run-id>),它会返回结构化的失败测试详情——名称、错误消息、stdout/stderr——来自 CI 上传到 Trunk Flaky Tests 的结果,且已过滤掉被隔离的已知不稳定测试。它只覆盖已运行并已上传的内容:对于在测试前就终止的任务(构建、安装、lint),请使用 engineering-analytics-run-failure-logs 或 gh run view --log。通过 /mcp → trunk(浏览器 OAuth)进行一次认证;无头环境则在 .mcp.json 的服务条目中添加带有 TRUNK_API_TOKEN 组织令牌的 Authorization: Bearer 头。要深入查看某个测试的不稳定历史,请移交给 fixing-flaky-tests,它涵盖 search-test 和 fix-flaky-test 工具。

在 Depot CI 上运行的后端测试

PR 可以将其后端测试路由到 Depot CI。 此时,GitHub Actions 运行仅承担中继角色:Django Tests Pass 会以“Backend tests on Depot CI concluded failure”失败,而 GitHub 运行日志不会显示更多内容。 重新运行该任务并非只读操作:它会重试失败和已取消的 Depot 任务并报告新的判定结果,因此禁止重新运行 CI 的规则同样适用于它。 如果 Depot 仍显示某个运行处于运行状态且已超过其任务超时时间,则该运行已卡住。重新运行会等待它,因此请先用 depot ci cancel 将其取消。 Relay the Depot verdict 步骤的日志会打印 Depot 运行 URL、每个失败步骤及其日志行,以及将 PR 送回 GitHub Actions 的标签。

使用 depot CLI(由 flox 环境安装)可从 Depot 读取更多失败信息。 运行 depot ci diagnose --org <org> --workflow <workflow>,其中两个 id 来自中继日志中的 Depot 运行 URL。 如需日志、制品和测试结果,请加载 depot-ci 技能。 当你没有中继日志时,其 references/posthog-check-run-semantics.md 可从某次提交的检查运行中获取 Depot id。 如果 depot 命令无法通过身份验证,请先检查 DEPOT_TOKEN:它会覆盖已保存的登录信息,因此错误的 token 会导致每条命令都失败。 如果没有该变量,请让用户运行 ! depot login --clear。 不要自己运行它:它会等待浏览器登录。 如果用户无权访问该 Depot org,请改为报告中继日志中的无 Depot 重试选项。

分类

日志中的信号类别首选操作
已提交的测试文件中出现 AssertionError、测试差异、FAILED test_...代码回归用 hogli test <path>::<test> 复现
测试在此处失败,但在 master 上或同一 PR 中重跑时通过不稳定测试对照 master 历史确认;如需修复,使用 fixing-flaky-tests
ruff、oxlint、stylelint、markdownlint、prettier 错误lint对改动的文件运行 hogli lint:python:fix 或 hogli format
mypy、pyright、tsc、typescript:check 错误类型检查在本地运行同样的检查,而不是完整测试套件
Chromatic / Storybook / Playwright 视觉差异、快照不匹配快照 / 视觉展示差异 URL;不要自动接受快照
manage.py migrate 错误、migrations:check 失败、缺少迁移迁移 / schemahogli migrations:check
OpenAPI schema 差异、生成的 API 类型不同步代码生成漂移hogli build:openapi
Cannot connect、ECONNREFUSED、address already in use、OOM、进程被杀、setup 步骤超时基础设施 / runner在将其称为偶发问题之前,先获取基准发生率(见下文)
startup_failure 结论、零步骤的 job,或返回 404 的日志 blob基础设施 / runner没有日志可读;查看 https://www.githubstatus.com/ 及其前后的运行记录
aget-get、uv sync、pnpm sync、docker pull、setup action 失败环境 / setup对比 .nvmrc、pyproject.toml、package.json、Dockerfiles
hogli lint:skills、hogli build:skillsskills 构建在本地运行同样的 hogli
SDK 兼容性检查、ci-survey-sdk-check、跨版本失败SDK 兼容性检查受影响包的 SDK 版本矩阵

如果多个信号匹配,请选择最具体的类别。例如,优先选择代码生成漂移而不是 lint,优先选择迁移而不是类型检查,优先选择快照 / 视觉而不是通用的 Playwright 测试失败。

基础设施与设置失败的基础发生率

“瞬时”是对任务失败频率的判断,因此不能仅凭一次运行就下结论。一个在测试运行前就终止的任务不会留下测试层面的证据:没有 FAILED 行,因此没有指纹,也没有 span,所以它永远不会作为一行出现在 broken_tests 或 flaky-tests 工具中。但它仍会作为任务结论可见,而摘要的 master-failures 部分正是按此分组,并且 engineering-analytics-run-failure-logs 仍会返回其失败行,因为它是按运行而非按测试读取的。从这里开始,而不是从某个测试开始。

与基于 span 的测试读取不同,这一项可以给你一个真实的发生率:仓库会记录每一次任务尝试,包括成功运行,因此分母是诚实的。products/engineering_analytics/skills/investigating-ci-failures/references/investigation-queries.md 中的 Query 7 可直接复制使用;该技能也负责更广泛的调查。

将结果解读为:

  • 百分比低,最近几小时大多为绿色 —— 瞬时。报告后继续。对于已排队的 PR,建议重新入队,而不是修改代码;自行运行 trunk merge 需要批准,依据上文的安全规则。
  • 最近几小时全红 —— 服务中断,而非偶发故障。应当说明这一点,并停止让人们重试。在将其归因于本仓库之前,先检查 https://www.githubstatus.com/;平台事故会让所有其他信号都成为症状。
  • 几分钟内一批运行同时失败 —— 一个共同原因,而非多个 bug。查找被许多合并继承的有问题的提交,或 GitHub 调度过载,后者会让运行在开始前以 startup_failure 失败,因此完全不留下日志。
  • 持续数天 —— 某个人负责的长期缺陷。即使每次出现看起来像噪声,也值得开一张工单。

本地复现

只运行能触发该失败的最窄命令。如果命令形式不明确,请阅读 .agents/skills/hogli/SKILL.md 和 hogli <command> --help。

类别复现指引
代码回归hogli test path/to/test.py::TestClass::test_method 或 hogli test <file.test.ts>
偶发测试移交给 fixing-flaky-tests 技能。
lint对改动过的文件使用失败的格式化工具/linter,例如 hogli format:python。
类型检查运行失败的检查器,例如 pnpm --filter=@posthog/frontend typescript:check。
快照 / 视觉运行特定的 Playwright 或 Storybook 工作流;如有需要,阅读 playwright-test。
迁移 / schemahogli migrations:check;仅在用户同意时运行迁移。
代码生成漂移hogli build:openapi。
基础设施 / runner无本地复现。获取基础发生率(见上文),报告并停止。
环境 / 设置仅在成本低且与改动文件相关时复现该设置步骤。
技能构建hogli lint:skills;若通过,则 hogli build:skills。

不要不带参数运行 hogli test。不要图省事运行 hogli nuke 或 hogli dev:reset。不要用 --no-verify 绕过钩子。

PostHog CI 说明

  • 大多数 PostHog 作业运行在 depot-ubuntu-latest 或 depot-ubuntu-latest-16 上。 Depot 与标准的 GitHub 托管运行器一样,通过 GitHub Actions UI / gh run view 暴露日志,因此请先在那里查看。
  • 当 Depot 运行器在作业中途挂掉时,GitHub 不会保留可读取的日志:该作业不显示 任何步骤,其日志 blob 返回 404。Depot 自己的仪表盘会保留该作业的页面, 并带有 GitHub 丢失的判定结果(OOM 终止、运行器丢失)。通过 chrome-devtools MCP 在浏览器中打开它。status.depot.dev 覆盖 Depot 自身发生故障的情况,而 depot-github-runners 技能负责分诊之外的运行器故障排查。
  • 一个 PR 可以将其后端测试路由到 Depot CI。 在那里读取失败信息,参见上文的“在 Depot CI 上运行的后端测试”。
  • 如果作业在 Checkout 完成之前就失败了(没有运行应用代码),请归类为 infra / runner。不要提出代码修复建议。
  • PostHog CI 经常将同一个测试类并行拆分到 N 个分片(backend-tests (3/10) 这种 形式)。请从具体失败的测试路径复现,而不是从分片索引。

报告形式

保持回复简短。包含一句可能原因,避免更深入的推测。

Target: PR #<num> - run <run-id> (<workflow file>)
Failing job:   <job name>
Failing step:  <step name>
Command:       <failing command>
Excerpt:
  <up to 40 lines, trimmed around the failure>

Classification: <class from the table>
Shadow run:     <yes | no>
Likely cause:   <one sentence>
Local repro:    <exact command, or "none">
Next action (needs your approval):
  - <push fix | rerun job | update snapshot | none>

如果分类为 infra / runner 或影子运行,请说明并停止; 不要提出代码更改。对于 infra / runner,请包含基础发生率以及 是否值得重试。

评论

支持 Markdown