深入理解 Flutter 仓库 LUCI 构建失败:基础设施故障与测试失败的分诊与重跑指南
在 Flutter 官方仓库中,绝大多数测试已经迁移到基于 LUCI(Layered Universal Continuous Integration,分层通用持续集成)的 CI 系统上运行。这套系统为 framework、engine 等多个 Flutter 仓库同时服务,覆盖 post-submit framework 构建面板(提交后测试)与 pre-submit framework 试跑构建(通过 LUCI Milo 查看)。本篇指南以 framework 为例,系统讲解当一次 LUCI 构建失败(build failure)发生时,如何通过构建面板快速判断失败类型(基础设施故障或真实测试失败),并按照官方推荐的处置流程完成问题上报、日志排查与任务重跑,帮助 PR 作者与仓库维护者高效解锁被阻塞的合并流程。读完本文,你将掌握 Flutter 基础设施上"紫色 = infra 故障、红色 = 测试失败"的颜色语义、构建总览页五个信息区的用法,以及 pre-submit / post-submit 两条重跑通道的完整操作步骤。
LUCI 构建失败的两大类型与面板颜色语义
在 Flutter 的构建面板中,每一个方框(box)代表一次构建结果,颜色是第一时间判断失败类型的信号:
| 颜色 | 含义 | 对应章节 |
|---|---|---|
| 紫色方框 | 基础设施故障(infra failure) | 下文"处理基础设施故障" |
| 红色方框 | 测试失败(test failure) | 下文"处理测试失败" |
| 绿色方框 | 构建成功 | — |
| 带感叹号图标的绿色方框 | 发生过 flake(最终重跑成功) | 见 Reducing-Test-Flakiness.md |
这两种类型在 post-submit 框架构建面板与 pre-submit 构建控制台中都遵循同样的颜色约定(官方文档分别以连续色块示例:紫色或红色方框夹在绿色成功结果之间)。在动手排查之前,先确认两个常用的构建入口:
- Flutter build dashboard(flutter-dashboard):框架提交后(post-submit)构建总览,同时包含物理设备上的 post-commit 测试。
- LUCI Milo dashboard:LUCI 统一的构建列表页,可按仓库、builder 分组查看。
关于 LUCI 在 Flutter 中的角色,仓库侧有更完整的说明:根据 dev/bots/README.md,LUCI 机器人对每次 PR 与提交运行 test.dart 脚本,统一完成 tools、framework 的测试;该基础设施由两份 .ci.yaml 配置驱动:framework 的 .ci.yaml 与 engine 的 engine/src/flutter/.ci.yaml。构建面板中每个 builder 的步骤、超时、标签与触发条件,都直接来源于这些 YAML 文件中对 target 的定义。
处理基础设施故障(Infra Failure)
什么是 infra failure
基础设施故障(infra failure)指与测试代码本身无关的失败,典型成因包括:
- 网络连接问题(transient network issue);
- 硬件故障或设备离线(如测试中途设备消失、
adb: device ... not found); - recipe 脚本破损(recipe breakage);
- CIPD 依赖问题(依赖包拉取或解析失败)。
这类失败在面板上以紫色方框呈现,例如 post-submit 构建面板中"连续绿色方块中夹着一个紫色方块",pre-submit 构建控制台中也一样(连续绿色矩形中出现一个紫色矩形)。
infra failure 构建的总览页结构
官方文档给出了一个典型示例:Linux color_filter_and_fade_perf__e2e_summary。打开其 LUCI overview 页面后,自上而下有五个信息区:
- (i) 该 builder 的历史构建列表链接:用于判断故障是否是偶发、是否在更早的提交上已经出现。
- (ii) 失败性质的快速预览:此处通常会直接标注本次失败是否为 infra failure。
- (iii) 该 builder 的步骤列表:这些步骤由右侧关联的 recipe 定义,可以从 dev/bots/README.md 了解 recipe 的工作方式——Flutter 为不同测试准备了多套 recipe,recipe 本质是带导入限制的 Python 脚本,并通过
recipe_modules共享公共动作;builder 使用哪套 recipe 由 infra 中的 builder 配置决定。 - (iv) 真正导致构建失败的步骤:由于 recipe 会串起多个步骤,前序步骤成功不代表整体成功,需定位到具体失败的 step。
- (v)
stdout详细日志入口:点击对应步骤的stdout即可阅读该步骤完整输出,故障根因信息通常在日志末尾的非零退出码附近。
infra failure 处置清单
遇到紫色失败,按以下顺序操作:
- 通过点击 (i) 检查该 builder 更早的构建,确认该 infra 故障是否已经反复出现;
- 检查 Flutter 的 infra bug 池(标签
team: infra且状态为 open),确认是否已有对应 issue; - 若没有现成 issue,使用 infra bug 模板 提交一个新的基础设施 bug;
- 若需要立即获得帮助,可在 Discord 的
hackers-infra频道提问(也可参考仓库侧的 Chat 了解社区沟通渠道约定); - 如果判定这只是一次 infra flake、需要重跑任务,则按场景执行下文"如何重跑失败的构建"。
处理测试失败(Test Failure)
如何识别测试失败
测试失败(test failure)是指构建本身运行正常、但某个或某些测试断言未通过。它在面板上以红色方框呈现:post-submit 构建面板中"绿色方块间夹着红色方块",pre-submit 构建控制台同理。
测试失败构建的总览页结构、五个信息区与 infra failure 完全一致,直接复用上文 (i)~(v) 的读图方法,其中需要重点关注的是 (ii) 的失败信息预览与 (v) 的 stdout 详细日志——它们共同决定这次失败究竟是真实代码缺陷还是偶发 flake。
测试失败处置清单
- 通过 (i) 检查该失败是否也出现在更早的构建或提交上,区分"新引入的回归"与"存量问题";
- 依据 (ii) 的错误信息与 (v) 的详细日志进行调试,判断失败是否由本次代码改动引起的真实测试失败。调试时可参考 Fix-failing-checks 中给出的典型输出形态——例如
flutter test框架捕获的异常通常形如:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching candidate
Actual: _TextWidgetFinder:<Found 0 widgets with text
"AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)": []>
Which: means none were found but one was expected
...
This was caught by the test expectation on the following line:
file:///b/s/w/ir/x/w/flutter/packages/flutter/test/widgets/async_test.dart line 115
════════════════════════════════════════════════════════════════════════════════════════════════════
这类输出会给出失败断言所在文件与行号、测试描述与完整堆栈,可以直接据此在本地复现并修复。
- 检查该问题是否已存在于 Flutter issues 列表;
- 检查是否是已知 flake:搜索标签为 c: flake 的 issues 列表,若命中则无需重复上报;
- 若无现成 issue 则按需提交新 bug;
- 如果需要重跑,参照 infra failure 一节中的步骤 6(重跑机制对两类失败通用)。
关于 flake 的完整治理流程(预防、每周自动检测、分诊、修复与重新启用),详见 Reducing-Test-Flakiness.md。该文同样依赖本指南介绍的方法——例如定位 test step 与阅读其 stdout 以区分"同一提交多次重跑后成功"与"单次运行内由 test runner 多轮重跑"两类 flake。
如何重跑失败的构建
无论是 infra flake 还是偶发测试失败,重跑(rerun)都是最常见的解锁手段。重跑入口按构建所属阶段分为两类。
pre-submit(PR 提交前检查)重跑
在 GitHub PR 的 check run 页面点击 Re-run 即可对该检查重跑:
- 该操作仅限
flutter-hackers组成员使用; - 若你没有相应权限,请在 Chat 文档介绍的 Discord
#hackers-infra频道请团队成员代为重跑。
post-submit(提交后主干构建)重跑
登录 framework build dashboard,点击对应任务方框,再点击 RERUN 按钮即可:
- 由于基础设施存在技术限制,该操作目前仅限 Googler(Google 内部成员);
- 非 Googler 需要到
#hackers-infra频道联系 Googler 协助重跑。
结合 .ci.yaml 理解 builder 与步骤的构成
要真正"读懂"一次 LUCI 失败,理解 builder 配置来源很有帮助。框架仓库的 builder 定义集中在根目录 .ci.yaml 中,每个 builder 条目包含以下关键字段,与失败排查直接相关:
| 字段 | 作用 | 排查关联 |
|---|---|---|
name |
builder 名称,如 Linux snippets |
与面板中失败方框的名称一一对应 |
recipe |
指定运行该 builder 的 recipe,如 flutter/flutter_drone、devicelab/devicelab_drone |
决定 (iii) 步骤列表的具体来源 |
bringup |
是否处于试验/灰度阶段(新接入测试先用 bringup: true 观察) |
值为 true 的 builder 失败不会导致整体树变红,是 flutter 治理 flake 的手段 |
timeout |
构建超时(分钟) | 超时类失败需结合 execution details 排查 |
properties |
传给 recipe 的属性,如 tags、shard 等 |
shard 划分了测试分片,便于在 stdout 中定位具体 shard |
runIf |
触发条件文件列表 | 判断该 builder 是否应被本次改动触发 |
以 .ci.yaml 中的 Linux snippets 为例,其片段形如:
- name: Linux snippets
recipe: flutter/flutter_drone
bringup: true
timeout: 30
properties:
tags: >
["framework", "hostonly", "shard", "linux"]
shard: snippets
runIf:
- dev/snippets/**
- dev/bots/**
- .ci.yaml
- engine/**
- DEPS
当某个 shard 报错时,日志中通常需要按 shard 名在 stdout 中检索对应输出;而 bringup: true 的 target(例如 .ci.yaml 中处于试验状态的 android_defines_test、external_textures_integration_test 等)失败时应优先怀疑是新接入测试本身的不稳定或环境准备问题,而非框架回归。
此外,根目录的 TESTOWNERS 记录了每个测试的归属团队,当需要为某个失败测试提交 bug 或指派分诊时,可据此快速找到负责方。
总结:一次构建失败的标准处置路径
综合上文,面对一次 LUCI 构建失败,推荐的完整路径是:
- 看颜色定性:紫色 = infra failure,红色 = test failure;
- 读总览页定位:用 (i) 判断历史、(iii)/(iv) 定位失败步骤、(v) 阅读
stdout日志找根因(可同时参考 Fix-failing-checks 中"查看测试输出"的具体导航方式); - 查池子去重:infra 故障查 team: infra 标签池,测试失败查 issues 列表与 c: flake 标签池;
- 分类处置:真缺陷 → 定位修复;infra 故障 → 上报或求助
#hackers-infra;确认为 flake → 按场景选择 pre-submit 的Re-run或 post-submit 的RERUN; - 复盘沉淀:反复出现的测试不稳定应进入 Reducing-Test-Flakiness 描述的标记
bringup: true、跟踪与重新启用流程。
如果失败的检查与主分支状态、Google 内部测试或 ci.yaml 校验相关,属于另一类 PR 阻塞场景,可直接查阅 如何修复 PR 的失败检查(Fix-failing-checks) 获取对应处理建议。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00