CI 每日失败汇总(Daily Fail Summary)机制解析)
MatterconnectedhomeipCI 每日失败汇总Daily Fail Summary机制解析【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip本文围绕 Matter 开源参考实现 connectedhomeip 仓库中 docs/ci-cd/tools/daily_fail_summary.md 所描述的 CI/CD 工具展开系统讲解这套每日失败汇总机制的完整构成由 GitHub Actions 定时工作流调度、Python 脚本采集 GitHub Actions 运行记录并做统计分析、YAML 失败定义文件辅助快速根因定位。读完本文你将掌握该工具的调度方式、核心脚本的完整数据流水线、失败定义文件的编写规则以及产出物每日通过率文档、CSV/SQLite 数据、失败日志归档的用途并可将其迁移复用到你自己的 CI 可观测性建设中。一、工具概览三个组件构成一条完整的每日巡检链路Daily Fail Summary 是 connectedhomeip 仓库 CI/CD 体系中的每日体检工具由三部分构成组件仓库相对路径职责工作流.github/workflows/recent_fail_summary.yaml按计划每天触发一次负责环境准备、执行脚本、更新文档与上传产物脚本scripts/tools/summarize_fail.py采集前一天的 workflow run 数据计算失败统计、通过率并做根因初判失败定义scripts/tools/build_fail_definitions.yaml维护错误签名 → 根因映射表让脚本能从失败日志中快速匹配根因该工具与仓库 CI 文档目录 docs/ci-cd/index.md 中罗列的 Spellcheck 等工具并列属于仓库自研的 CI 质量观测工具。其核心价值在于每天对前一天所有 workflow run 做一次盘点输出哪些 PR 在哪个工作流上挂了、大概是什么原因、每个工作流通过率是多少从而让 CI 维护者不用手动翻 logs 就能掌握失败全貌。二、调度机制每天 00:10 自动巡检也支持手动触发工作流 .github/workflows/recent_fail_summary.yaml 的调度配置如下name: Recent Fail Summary on: schedule: - cron: 10 0 * * * workflow_dispatch: concurrency: group: ${{ github.workflow }}schedulecron10 0 * * *UTC 时间每天 00:10 运行一次即Runs once per day的落地实现。需要说明的是GitHub Actions 的schedule事件在仓库较长时间无提交时可能被延迟或跳过workflow_dispatch提供了手动补救手段。workflow_dispatch允许在 GitHub 仓库的 Actions 页面手动触发便于随时补跑当天或某天的统计。concurrency.group: ${{ github.workflow }}同一时刻只允许一个实例运行避免并发触发导致产物互相覆盖脚本会写入fail_run_list.json、workflow_pass_rate/等固定路径文件。任务运行在ubuntu-latest上并声明permissions: write-all需要写分支、上传 artifact 的权限。执行步骤依次为steps: - uses: actions/checkoutv7 - run: pip install pandas python-slugify pyyaml tabulate - name: Run Summarization Script run: python scripts/tools/summarize_fail.py env: GH_TOKEN: ${{ github.token }} - name: Update Docs uses: test-room-7/action-update-filev2 with: file-path: docs/daily_pass_percentage.md commit-msg: Update daily pass percentage github-token: ${{ secrets.GITHUB_TOKEN }} branch: daily_pass_percentage - name: Upload Logs uses: actions/upload-artifactv7 with: name: workflow-fail-summary path: | fail_run_list.json all_run_list.json recent_fails.csv recent_fails_frequency.csv failure_cause_summary.csv workflow_pass_rate.csv workflow_pass_rate.sqlite3 recent_fails_logs workflow_pass_rate retention-days: 5几个值得注意的实操细节依赖安装脚本运行依赖pandasDataFrame 处理、python-slugify把 PR/工作流名转成安全路径、pyyaml解析失败定义文件、tabulateDataFrame.to_markdown输出 Markdown 表格。这四者是脚本能跑通的前提。鉴权脚本内部大量调用gh run list/gh run view通过环境变量GH_TOKEN: ${{ github.token }}注入令牌无需额外创建 secret。文档更新统计结果写入docs/daily_pass_percentage.md后由test-room-7/action-update-filev2提交到独立的daily_pass_percentage分支注意不是 master 分支commit message 固定为 Update daily pass percentage。因此当前仓库工作区中看不到该文件——它是运行时生成并推送到专属分支的产物。产物保留所有中间数据以workflow-fail-summary为名的 artifact 上传retention-days: 5即保留 5 天与脚本每日一份、临时缓存的定位一致Creates temporarily cached artifacts for easy data parsing。三、核心脚本一天的失败数据是如何被盘点出来的脚本 scripts/tools/summarize_fail.py 是整个机制的大脑。它围绕一个关键时间变量工作yesterday (datetime.datetime.now() - datetime.timedelta(days1)).strftime(%Y-%m-%d)即每次运行只处理昨天now - 1 day格式%Y-%m-%d产生的工作流运行记录保证每日一份、互不重叠。脚本启动时还会加载失败定义文件with open(scripts/tools/build_fail_definitions.yaml) as fail_defs: error_catalog yaml.safe_load(fail_defs)3.1 失败清单收集只捞失败的运行main()的第一步是列出前一天所有失败的 workflow runsubprocess.run( fgh run list -R project-chip/connectedhomeip -b master -s failure -L 500 --created {yesterday} --json databaseId,displayTitle,startedAt,workflowName fail_run_list.json, shellTrue)对应参数含义-R project-chip/connectedhomeip目标仓库-b master只看 master 分支-s failure仅取结论为 failure 的运行-L 500最多 500 条防止单日失败过多导致列表爆炸--created {yesterday}只统计前一天--json databaseId,displayTitle,startedAt,workflowName导出 run 的 ID、展示标题、开始时间、工作流名四个字段用于后续分析。随后用 pandas 读入 DataFrame将列重命名为[ID, Pull Request, Start Time, Workflow]控制台打印Recent Fails From {yesterday}表格并落盘recent_fails.csv。3.2 失败频率统计哪个工作流是重灾区frequency df[Workflow].value_counts(normalizeTrue).mul(100).round().astype( str).reset_index(namePercentage) frequency.to_csv(recent_workflow_fails_frequency.csv)value_counts(normalizeTrue)计算每个工作流在失败总数中的占比乘以 100 取整后转成字符串如 50再输出Share of Recent Fails by Workflow表格并落盘recent_workflow_fails_frequency.csv。这能一眼看出 CI 失败是否集中在个别工作流上。3.3 全量运行收集与单工作流通过率为了计算通过率脚本还需要所有运行而不只是失败的因此用更大的上限再次拉取subprocess.run( fgh run list -R project-chip/connectedhomeip -b master -L 5000 --created {yesterday} --json workflowName all_run_list.json, shellTrue)这里-L 5000是 500 的十倍上限用于覆盖前一天所有工作流的总运行数。随后对每个工作流调用pass_fail_rate(workflow)def pass_fail_rate(workflow): workflow_fail_rate_output_path fworkflow_pass_rate/{slugify(workflow)} if not os.path.exists(workflow_fail_rate_output_path): os.makedirs(workflow_fail_rate_output_path) subprocess.run( fgh run list -R project-chip/connectedhomeip -b master -w {workflow} -L 500 --created {yesterday} --json conclusion {workflow_fail_rate_output_path}/run_list.json, shellTrue) else: log.info(This workflow has already been processed.)以slugify(workflow)规范化后的工作流名为目录逐工作流把最近 500 次运行的conclusion字段存入workflow_pass_rate/slug/run_list.json用目录是否存在作为是否已处理的去重标记避免重复调用gh也体现了前文concurrency配置的意义slugify来自python-slugify库作用是把含空格、标点的 workflow 名转成安全合法的文件系统目录名。3.4 失败根因初判错误签名匹配对每一条失败记录脚本会拉取该次运行的完整失败日志并做签名匹配def process_fail(workflow_id, pr, start_time, workflow): output_path frecent_fails_logs/{slugify(pr)}/{slugify(workflow)}/{slugify(start_time)} os.makedirs(output_path) subprocess.run( fgh run view -R project-chip/connectedhomeip {workflow_id} --log-failed {output_path}/fail_log.txt, shellTrue) root_cause Unknown cause with open(f{output_path}/fail_log.txt) as fail_log_file: fail_log fail_log_file.read() workflow_category workflow.split( - )[0] if workflow_category in error_catalog: for error_message in error_catalog[workflow_category]: if error_message in fail_log: root_cause error_catalog[workflow_category][error_message][short] break return [pr, workflow, root_cause]gh run view {workflow_id} --log-failed只拉取失败步骤的日志保存到recent_fails_logs/PR slug/workflow slug/start_time slug/fail_log.txt实现每一条失败都有原始日志留档根因匹配的关键技巧workflow_category workflow.split( - )[0]即取工作流名中第一个 - 之前的部分作为分类键例如Build example - Linux - ...会归类到Build example再用分类名在error_catalog即 build_fail_definitions.yaml 解析结果中查找在日志中逐个匹配该分类下的错误签名子串命中即取short字段作为根因全部未命中则根因为默认值Unknown cause由于签名匹配用的是子串包含判断error_message in fail_log因此错误签名只需是日志中稳定出现的典型片段即可不必是完整异常栈。汇总后main()输出Likely Root Cause of Recent Fails表格并落盘failure_cause_summary.csv。3.5 通过率汇总最终产物 docs/daily_pass_percentage.md脚本最后汇总每个工作流的通过率for workflow in next(os.walk(workflow_pass_rate))[1]: info pd.read_json(fworkflow_pass_rate/{workflow}/run_list.json) info info[info[conclusion].str.len() 0] # 过滤掉仍在运行、结论为空的记录 pass_rate[workflow] [info.value_counts(normalizeTrue).mul(100).round()[success]] pass_rate pd.DataFrame.from_dict(pass_rate, index, columns[Pass Rate]).sort_values(Pass Rate) pass_rate.to_markdown(docs/daily_pass_percentage.md) pass_rate_sql sqlite3.connect(workflow_pass_rate.sqlite3) pass_rate.to_sql(workflow_pass_rate, pass_rate_sql, if_existsreplace) pass_rate.to_csv(workflow_pass_rate.csv)这段代码完成了三类输出docs/daily_pass_percentage.md用DataFrame.to_markdown生成 Markdown 表格即原文档所说 saves a daily pass percentage list of all workflows随后由工作流提交到daily_pass_percentage分支形成可公开查阅的每日通过率报告workflow_pass_rate.sqlite3通过 SQLite 持久化同一份数据便于历史累计与 SQL 查询workflow_pass_rate.csv机器可读的 CSV 版本。需要注意的是脚本对conclusion为空的记录做了过滤str.len() 0避免把仍在进行中、尚未出结论的运行计入分母若某工作流数据缺失则通过率记为0.0。四、失败定义文件让根因定位从人工翻日志变成自动匹配scripts/tools/build_fail_definitions.yaml 是根因初判的知识库采用三层结构分类对应工作流名前缀→ 错误签名 → {short, detail}。当前仓库内置的失败定义如下CodeQL: No space left on device: short: Ran out of space detail: Exception with signature No space left on device Check that the disk containing the database directory has ample free space.: short: Ran out of space detail: Fatal internal error with message indicating that disk space most likely ran out Build example: Could not find a version that satisfies the requirement: short: Requirements issue detail: Unable to install a requirements in Python requirements.txt No module named: short: Missing module detail: Expected module was missing Full builds: No space left on device: short: Ran out of space detail: Exception with signature No space left on device字段语义顶层键工作流分类名与脚本中workflow.split( - )[0]的结果对应例如CodeQL、Build example、Full builds第二层键错误签名即失败日志中出现的典型文本片段子串匹配short根因的一句话摘要直接出现在failure_cause_summary.csv的 Cause of Failure 列中detail补充说明便于人工确认。从现有定义可以看出实际维护中沉淀的几类高频根因CI 运行环境磁盘耗尽No space left on device在 CodeQL 与 Full builds 中都有、Python 依赖解析失败Could not find a version that satisfies the requirement、模块缺失No module named。仓库文档明确说明失败定义可以追加到这个文件里Fail definitions can be added to the file defined above to allow fast root cause determination of any fail with an error message任何带稳定错误信息的失败都可以通过新增一条签名映射实现根因的自动快速判定。五、数据流水线与产物全景综合工作流与脚本一次每日运行产出的数据流可归纳如下阶段关键命令/文件产出失败清单gh run list ... -s failure -L 500fail_run_list.json、recent_fails.csv失败频率pandasvalue_countsrecent_workflow_fails_frequency.csv全量运行gh run list ... -L 5000all_run_list.json单工作流运行gh run list ... -w workflow -L 500workflow_pass_rate/slug/run_list.json失败日志与根因gh run view ... --log-failed 签名匹配recent_fails_logs/PR/workflow/time/fail_log.txt、failure_cause_summary.csv通过率汇总pandas SQLite Markdowndocs/daily_pass_percentage.md推送到daily_pass_percentage分支、workflow_pass_rate.sqlite3、workflow_pass_rate.csv全部中间文件以workflow-fail-summaryartifact 上传并保留 5 天。这套设计的好处是所有原始数据JSON/CSV/SQLite/日志都临时缓存下来任何人下载 artifact 后都能复现当天的统计结果或做二次分析而daily_pass_percentage分支上的 Markdown 则提供了对外可见、可长期追溯的每日通过率报告。六、现状待办与改进方向原文档还明确列出了该工具当前的状态与后续计划可作为贡献者参与该仓库 CI 改进的切入点To Do待办持续维护失败签名列表build_fail_definitions.yaml覆盖所有常见失败的原因在失败汇总脚本中加入 Darwin 测试的失败定义起步对象是 Test Reliable Message Protocol可译作测试可靠消息协议相关用例。这意味着目前 macOS/Darwin 平台上的失败还没有被签名覆盖根因匹配会落到Unknown cause。Improvement Ideas改进构想让脚本产出的 artifact 更易被发现和获取方便社区所有人共享使用通过 Slack 机器人以短消息形式每日推送失败摘要让维护者无需进入仓库页面即可获知失败概览。这两个方向分别对应产物可见性与推送渠道的增强属于将该机制从仓库内部工具升级为团队日常协作流的演进路线。七、小结Daily Fail Summary 是 connectedhomeip 仓库 CI 可观测性的一个轻量但完整的范例用一份 cron 工作流 一个 Python 脚本 一个 YAML 知识库实现了每日自动盘点失败 → 频率统计 → 根因初判 → 通过率报告 → 产物归档的闭环。它的核心设计——错误签名驱动的根因匹配、slug 化路径的日志归档、独立分支承载每日报告、5 天保留的临时产物——对任何希望为自身 CI 建立失败巡检能力的团队都具有直接借鉴价值。若要在本地或自建环境中复现这套流程只需保证安装了ghCLI 且具备目标仓库的读取令牌再以python scripts/tools/summarize_fail.py运行脚本即可注意脚本中-R project-chip/connectedhomeip等仓库参数需要按实际情况调整。【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考