ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ClickHouse Flaky Test Issue 治理实践:CI 自动标记、失败历史查询与 close-flaky-issues 清扫流程

ClickHouse Flaky Test Issue 治理实践:CI 自动标记、失败历史查询与 close-flaky-issues 清扫流程 ClickHouse Flaky Test Issue 治理实践CI 自动标记、失败历史查询与 close-flaky-issues 清扫流程【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse本文围绕 ClickHouse 仓库中的close-flaky-issuesSkill.claude/skills/close-flaky-issues/SKILL.md展开它是一套用于审计并关闭flaky test不稳定测试GitHub issue 的半自动化流程。读完本文你将理解 ClickHouse CI 如何为不稳定测试自动生成 issue、如何用play.clickhouse.com的checks表批量核对 master 分支上的失败历史以及如何安全地执行打印范围表 → 关闭 issue → 发布总结报告的完整清扫sweep同时掌握每个步骤中若干容易踩坑的细节jq 容错、SQL 引号转义、空列表防护等。背景flaky test issue 是如何进入仓库的理解这个 Skill 的前提是先理解它清扫的对象——带flaky test标签的 issue 是由 CI 机器人自动生成的。从源码结构看这条链路在仓库中有清晰的证据创建当 master 上某测试失败、且被判定为 flaky/broken test 时ci/jobs/scripts/check_ci.py 中的CreateIssue.create_gh_issue_on_flaky_or_broken_test会拼装 issue 正文字段包括Test name:、Failure reason:、CI report:指向 S3 测试报告、Failing test history:指向play.clickhouse.com的测试统计页并打上testingflaky test双标签。该方法在正文头部强制插入警告_Important: This issue was automatically generated and is used by CI for matching failures. DO NOT modify the body content. DO NOT remove labels._这解释了 Skill 规则第一条绝不修改 issue 正文或标签的由来——CI 后续要解析这些字段。消费CI 每次任务结束时ci/praktika/native_jobs.py 的_check_and_link_open_issues会从 S3 下载 flaky issue 目录catalog对失败的测试结果逐一调用issue.check_result(result, job_name)匹配逻辑在 ci/praktika/issue.py 的_check_flaky_test_match中实现——它比对测试名支持 pytest 参数化名称的宽松匹配并且若 issue 正文里写了Failure reason:还要求该字符串真实出现在测试输出里两者都满足才把结果标记为flaky。这意味着只要 issue 还开着同名测试再次失败就会被标记为 flaky 而不阻塞 CI反之一旦测试真正修好issue 就成了无人认领的僵尸——这正是该 Skill 要解决的问题。目录收集每小时调度工作流 ci/workflows/hourly.py 中有一个名为 Collect flaky tests 的 job执行python3 ./ci/praktika/issue.py --collect-and-upload把 open 状态含最近 8 小时内新关闭的testing标签 issue 收集成 catalog 上传到 S3供各 job 在收尾时下载匹配。正因为关闭 issue 是安全的——测试若再次失败CI 会自动重新打开这个 Skill 才敢在不逐条人工确认的前提下批量执行gh issue close。Skill 入参与总体目标Skill 定义.claude/skills/close-flaky-issues/SKILL.md声明了两个可选参数参数含义默认值$0threshold-days测试在 master 上必须无失败的天数窗口窗口内 0 失败才具备关闭资格14$1dry-run传入dry-run时只打印可关闭候选表并停止不调用gh issue close其他任何值或不传则执行完整清扫完整执行清扫目标遍历所有 open 的flaky testissue关闭同时满足以下两个条件的 issue在阈值窗口内 master 上失败次数为 0尽力而为能关联到一个修复 commit——关联到 commit 只是更好不是关闭的必要条件。步骤 1列出 open 的 flaky test issuemkdir -p tmp/flaky gh issue list --repo ClickHouse/ClickHouse --label flaky test --state open \ --limit 200 --json number,title,body tmp/flaky/issues.json每个 issue 正文遵循固定格式Test name: test_name Failure reason: reason CI report: url Failing test history: play.clickhouse.com 链接其中 # 号后是 base64 编码的 SQL用jq提取(number, test_name)对jq -r .[] | [.number, ((.body | capture(Test name: (?n.))? | .n) // )] | tsv tmp/flaky/issues.json这里capture(...)后面的?是必需的不带它任何body为null或正文中不含Test name:的 issue 都会让 jq 抛错中止整个管道并静默丢掉该 issue 之后的所有条目。加上?后这类行产出空字符串随后可以回退到两个来源——issue 标题老 issue 把测试名写在标题里或Failing test historyURL 中#后面的 base64 SQL。这一点与 CI 侧代码相互印证ci/praktika/issue.py 中Issue.extract_test_name就是临时辅助方法等待所有 CI issue 都统一在正文中携带测试名之前的过渡解析优先匹配NNNNN_namestateless 测试或test_*pytest两种标题模式parse_issue_body_fields则解析正文中的Test name:/Failure reason:等字段正文缺失时同样回退标题。步骤 2一次性批量查询 CI 失败历史对提取出的所有测试名只发一次批量查询打到play.clickhouse.comchecks表通过play用户公开可读curl -sS https://play.clickhouse.com/?userplay --data-binary SELECT test_name, countIf(test_status IN (FAIL,ERROR)) AS failures_90d, countIf(test_status IN (FAIL,ERROR) AND check_start_time now() - INTERVAL 30 DAY) AS failures_30d, countIf(test_status IN (FAIL,ERROR) AND check_start_time now() - INTERVAL 14 DAY) AS failures_14d, countIf(test_status IN (FAIL,ERROR) AND check_start_time now() - INTERVAL 7 DAY) AS failures_7d, maxIf(check_start_time, test_status IN (FAIL,ERROR)) AS last_fail FROM checks WHERE check_start_time now() - INTERVAL 90 DAY AND test_name IN ( test1, test2, ... ) AND (pull_request_number 0 OR base_ref IN (master,)) GROUP BY test_name ORDER BY failures_7d DESC, failures_14d DESC FORMAT TabSeparatedWithNames 执行前有两条硬性防护都是真实会踩的坑空列表防护如果 jq 一个非空测试名都没提取出来没有 open 的 flaky test issue或正文都缺Test name:test_name IN ()是非法 SQL会直接中止清扫。此时应跳过查询直接报告 nothing to close。单引号转义按 ClickHouse SQL 规则字符串字面量中的必须写成。含引号的测试名例如参数化集成测试test_foo[ab]不转义会生成非法 SQL甚至更糟——被解析成另一个列表。拼接前对每个名字应用s///g。查询结果解读的三个要点pull_request_number 0 OR base_ref IN (master,)把统计限定在 master 提交以及指向 master 的 PR 上丢弃 fork PR 的噪音空字符串分支是为了兼容早期base_ref未填充的 direct-master 行。查无结果的测试0 行意味着它在看板窗口内从未在 master 上失败过——这类同样可以关闭。从未失败的测试last_fail默认为1970-01-01maxIf在无匹配行时的零值。步骤 3按阈值分类对每个 issue依据failures_threshold-daysd判定窗口内0次失败 →关闭候选窗口内 1次失败 →保持 open默认阈值 14 天激进的清扫用 7 天保守的用 30 天。14 天是清理陈年垃圾与避免过早关闭之间的平衡点——测试可能存在数周的休眠期后突然复活。步骤 4尽力寻找修复 commit对每个关闭候选在 git 历史中做两次搜索均为 best-effort找不到也不阻塞关闭# 搜索在提交信息中点名该测试的 fix flaky commit git log origin/master --sincedate 90 days ago --format%H %s \ --greptest_name_fragment -i # 查看测试文件近 90 天的直接改动 git log origin/master --sincedate 90 days ago --format%H %s -- test_path测试路径的推断规则集成测试test_X/test.py::...形式→ 目录tests/integration/test_X/本仓库中该目录下有大量test_*/test.py与configs/*集成用例stateless 测试NNNNN_name形式→tests/queries/0_stateless/NNNNN_name.*.sql 对应.reference文件。并非每个候选都有显式修复 commit——有些测试是靠周边基础设施变更顺带稳定下来的这类同样关闭只是不附 commit 引用。步骤 5先打印范围表再执行发出任何gh issue close之前必须先打印一张表包含待关闭 issue若找到修复 commit附带上保持 open 的 issue附上失败次数。调用者触发 Skill 本身即视为已授权关闭所以不要追加交互确认——打印完直接进入下一步。这张表的意义是事后可抽查而非交互式审批想要只预览不变更的用户应传dry-run作为第二个参数Skill 应在此处停止。步骤 6关闭 issue 并留下说明性评论对每个可关闭 issue评论必须说清是什么修复了它以及最近一次失败有多久以前# 找到修复 commit 时 gh issue close N --repo ClickHouse/ClickHouse \ --comment Fixed by SHA (\commit subject\). No failures on master in the last D days (last seen YYYY-MM-DD). Closing. # 没有找到具体修复 commit 时 gh issue close N --repo ClickHouse/ClickHouse \ --comment No failures on master in the last D days (last seen YYYY-MM-DD). Closing as no longer flaky; will reopen automatically by CI bot if it fails again.两条格式细节评论中使用 40 位完整 SHA——短 SHA 日后可能产生歧义测试名、commit subject 与各类标识符按项目 CLAUDE.md 的样式规则用反引号包裹。步骤 7收尾报告任务结束时的总结应包含关闭了多少个 issue并区分关联到修复 commit与无显式修复但已稳定两类仍保持 open 的数量是否还遗留高频肇事者例如每周失败超过 5 次的测试——这类问题需要的不是清扫而是找到 owner 去修。规则清单RulesSkill 文档末尾给出的一组硬规则值得逐条对照理解其动机规则动机可从仓库印证绝不修改 issue 正文或标签正文头部即声明DO NOT modify the body content. DO NOT remove labels.见 ci/jobs/scripts/check_ci.py因为 CI 的_check_flaky_test_match依赖解析Test name:/Failure reason:字段做匹配阈值窗口内有任何一次失败就不关只要窗口内还有失败测试就仍算 flaky不要重新打开带人工理由关闭的 issue重新打开由 CI bot 负责工作文件一律放tmp/flaky/不要用/tmp项目 CLAUDE.md 的约定Skill 文档引用默认以failures_14d 0为准测试可能数周休眠后复活14 天是平衡点不要仅凭测试文件是否存在来判断测试存在也可能一直通过测试被删了 issue 也可能还没关。失败历史才是唯一事实来源source of truth小结这套机制为什么能安全地批量关 issue从源码结构看整个闭环可以概括为三个角色CI bot 创建 issueci/jobs/scripts/check_ci.py并保证正文格式稳定CI 每小时收集 issue 目录ci/workflows/hourly.py → ci/praktika/issue.py每次 job 收尾时用它把同名失败标记为 flakyci/praktika/native_jobs.pyclose-flaky-issues Skill 定期清扫用checks表的失败历史做唯一裁决依据把已稳定的 issue 关掉并留下带证据的评论。三者共同构成的不变式是issue 的开/关状态永远可以被checks表的历史失败数据校验——关错了bot 会重新打开不关flaky 标记会长期豁免该测试的失败。因此这个 Skill 的设计哲学是以失败历史为唯一事实来源、以打印范围表替代交互审批、以评论留证这也使其可以直接复用到任何具备CI 自动生成 issue 公开失败历史库的仓库中。注本文中的 SQL 查询面向play.clickhouse.com公开实例checks表结构与字段名以该实例实际 schema 为准阈值、dry-run 等行为均以 Skill 文档 当前内容为准。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表