Flutter 仓库 CI 最佳实践指南:合入、标签、重跑与测试分片的高效策略
本篇指南围绕 Flutter 官方仓库中团队日常使用其内部 CI/CD 系统的高效技巧展开,内容取自仓库文档 docs/infra/Ci-Best-Practices.md。你可以学到:如何处理 "This branch is out-of-date" 提示、为什么优先用 auto-submit 标签代替手动点击 merge、revert 标签的正确打开方式、重跑测试套件为何优于全量重试,以及在 .ci.yaml 中新增/重命名/重新分片测试目标所要付出的真实成本与完整操作流程。
阅读前提:这是面向 Flutter 内部 CI 的补充指南
本文档是 Flutter 内部 CI/CD 系统的补充性实践建议,并非面面俱到的完整教程,而是把"如何尽可能高效"的实战经验沉淀成文。它约束的对象主要是 flutter/flutter 主框架仓库及其关联的 flutter 官方仓库(本仓库即对应框架源码与 CI 配置的镜像,根目录存在一份真实可查的 .ci.yaml,全文共 7800 余行,是本文所有分片讨论的实例来源)。
这套 CI 体系在 .ci.yaml 文件头部有自述:
# Describes the targets run in continuous integration environment.
# Flutter infra uses this file to generate a checklist of tasks to be performed
# for every commit.
即:每个提交都要按 .ci.yaml 里声明的 targets 清单执行一套任务检查。其中 flutter_drone recipe 会把 shard 键对应的分片委托给仓库内 dev/bots/test.dart 执行。相关的更多流程性文档位于 docs/infra/Adding-a-new-Test-Shard.md、docs/infra/Autosubmit-bot.md 与 docs/infra/Landing-Changes-With-Autosubmit.md,可作为本文的配套阅读。
"This branch is out-of-date" 并不(总是)意味着失败
当至少有一个 commit 被合并进 PR 的祖先分支时,GitHub 会自动生成 "This branch is out-of-date" 提示。在 Flutter 官方仓库中,这只是一条警告,并不会阻止合并,因此不必一看到它就着急 rebase。
但有两种边界情况需要合并/变基:
- 你的 PR 基于的那个 commit 恰好导致某个测试失败。如果祖先分支上已经合入了前向修复(forward fix)或 revert,建议 rebase 你的 PR 以纳入该修复,避免测试持续红。
- 你的 PR 基于的 commit 已经超过几天。为保证测试跑在最新代码之上,建议 rebase,让 CI 结果对当前主干有意义。
对照到本仓库的实际运作:flutter/flutter 在合并层面还有一套独立的 merge queue(自动提交队列)机制,相关设计见 docs/infra/merge_queue.md,与 GitHub 自带的过时提示是两套不同逻辑,勿混淆。
优先给 PR 打 auto-submit 标签,而不是点 "merge"
auto-submit 是一个特殊标签:打上后,当测试全部通过时,PR 会被自动合并(或排队等待合并)——在 flutter/flutter 的场景下是进入合并队列等待。这是官方推荐的合并方式,开发者无需守在电脑前盯着绿灯再手动点击。
如果有一个或多个测试失败,而你确信失败与你的 PR 无关(即属于 flake 偶发失败),请不要反复 push 空提交去触发全量重试,而应参考下文"优先重跑测试套件"一节处理。
auto-submit 背后的自动化逻辑由 Autosubmit bot 承担,其完整校验规则(代码评审数量、check run 状态、合并可行性、代码冲突检测)记录在 docs/infra/Autosubmit-bot.md,其典型工作流为:
- 提交 PR;
- 后台自动对改动运行大量测试;
- 开发者打上
auto-submit标签,表示"测试通过后请自动合入"; - 全部校验成功后,bot 完成合并。
优先用 revert 标签回滚最近合入的提交
当合入的 commit 引发 CI 失败时,与其手动构造反向提交,不如用 revert 标签:该标签会触发自动创建一条回滚该 commit 的新 PR,且绕过大部分测试,以最快速度让树恢复绿色。
使用前必须满足两个条件:
- 在 PR 上追加一条以
reason for revert:开头的评论,例如:
reason for revert: This breaks the build, see XYZ link.
- 与管辖该仓库的 Google 团队协调后再使用
revert标签。因为并非所有场景都适合 revert,误用可能造成困惑。
Autosubmit bot 对 revert 请求还有额外约束(详见 docs/infra/Autosubmit-bot.md):发起人需为 flutter-hackers 团队成员;目标 PR 必须是 24 小时内合入的;必须提供以 Reason for revert: 开头的理由,否则标签会被移除。在 flutter/flutter 主仓库中,support_no_review_revert 通常为 true,即 revert 请求不强制要求先评审。
重跑单个测试套件,比全量重跑所有测试便宜得多
在 flutter/flutter 中,一次完整的全量测试运行要消耗 100~200 台虚拟机,耗时 30~60 分钟。这是极其昂贵的资源开销,应尽量规避。
而 push 一个新 commit,或点击 GitHub 内置的 merge 按钮,都会重新触发该 PR 的全量测试。有时确有必要,但大多数场景下,只重跑一个(或少数几个)失败的测试套件,成本要低得多。
实操建议:对疑似 flake 的失败,优先进入失败任务的详情页使用"Re-run"按钮只重跑对应套件,而不是用空 commit 或改动触发的全量重跑。这与 docs/infra/Reducing-Test-Flakiness.md 中对偶发失败的治理思路一致——把资源留给真正需要验证的代码,而不是花在重复的绿跑上。
新增测试目标的成本:每个 target 都会独占一台 VM
Flutter 团队用于跑测试的资源是有限的。文档记录(截至 2025/05/23)flutter/flutter 的 presubmit 资源池大致为:
| 资源池 | 规模 |
|---|---|
| Linux VM(Ubuntu) | 约 301 台 |
| ARM64 Mac VM(Mac-14) | 约 131 台 |
| Windows VM(Windows-10) | 约 134 台 |
而用于真机测试的 device lab 资源比这些虚拟机组更加稀缺。
新增一个测试目标(test target)意味着:整台 VM 将在克隆仓库、搭建测试基础设施、运行测试套件的整个时间段内被独占,通常耗时 15~30 分钟,部分集成测试甚至更久。虽然好的测试套件很重要,但在新增 target 前请先自问:
- 我的测试目标必须在多个平台上跑吗?
- 是否存在尚未逼近超时上限的既有测试目标,可以把测试加进去,从而复用同一台 VM?
在批量新增测试目标、或拿不准如何组织时,建议先咨询基础设施团队(team-infra)。真实配置形态可对照根目录 .ci.yaml:例如其中 Linux snippets、Linux flavors_test_linux 等 target 均显式携带 bringup: true,且通过 properties.shard 声明分片、通过 runIf 声明触发条件、通过 timeout(单位分钟)约束运行时长——每新增一行这样的 target,都意味着资源池多一份长期占用。
重命名/重新分片测试的成本:为什么可能花掉 4 个 PR
本节建议仅适用于 flutter/flutter 仓库。
由于测试基础设施的运作方式,重命名或重新分片(resharding)测试非常耗时,能不做就不做,必须做时也要谨慎协调。核心约束有三条:
- 新分片必须先以
bringup: true加入,这意味着它不会在 presubmit 中运行,postsubmit 阶段也不会触发树关闭; - 分片稳定运行后要改为
bringup: false,这需要第二个 PR; - 在主干(
master)删除(或重命名)一个分片,会导致该测试在所有 release candidate 分支中被静默跳过;必须把.ci.yamlcherry-pick 进每个 release candidate 分支才能恢复覆盖率。
场景示例:从 2 个分片扩到 3 个分片
假设已有配置如下(注意:每个 target 对应一个独立的 LUCI builder):
targets:
- name: Linux web_tests_1_2
shard: web_tests
subshard: "0"
- name: Linux web_tests_2_2
shard: web_tests
subshard: "1"
现在想增加到第三个分片(实质上是重命名所有 target):
targets:
- name: Linux web_tests_1_3
shard: web_tests
subshard: "0"
- name: Linux web_tests_2_3
shard: web_tests
subshard: "1"
- name: Linux web_tests_3_3
shard: web_tests
subshard: "2"
这需要以下步骤:
- 在
.ci.yaml中加入 3 个新分片,并各自设置bringup: true; - 等待新分片稳定通过;
- 从
.ci.yaml中删除旧分片,并将新分片改为bringup: false; - 将
.ci.yamlcherry-pick 进每个 release candidate 分支以恢复测试覆盖。
整个过程最多可能需要 4 个 PR,并伴随与 release 团队的大量协调。
与官方"新增分片"流程的相互印证
Adding-a-new-Test-Shard.md 对"从零新增一个框架测试分片"给出了标准顺序,可与上文互相印证:
- 先在框架仓库的
dev/bots/test.dart中注册新分片(对既有测试做分片则无需改动 test.dart); - 在
.ci.yaml中新增对应 builder,确保shard与subshard/subshards属性与 test.dart 一致,并始终标记为bringup: true(此阶段该 target 不会进 presubmit); - 在 Flutter build dashboard 上观察新分片:连续 50 次无 flake 的绿跑后,flake bot 会自动开出移除
bringup: true的 PR,使该测试具备阻止树的能力,并开始自动进入 presubmit(除非显式写了presubmit: false)。flake bot 每周三运行一次。
注意:若新 target 是从既有 target 重命名而来,则无需走完整的 bringup 流程。
这些规则在本仓库根目录 .ci.yaml 中有大量活样例:例如 Linux snippets(.ci.yaml 第 701 行附近)与 Linux flavors_test_linux(第 736 行附近)都处于 bringup: true 状态;而 Linux analyze(第 365 行起)则是一个未启用 bringup、直接以 shard: analyze 参与 presubmit 的常规 target。分片命名方面,Linux build_tests_1_5~build_tests_5_5、tool_integration_tests_1_7~6_7 等均采用 name: <平台> <shard>_<subshard> 的约定,与文档示例 Linux web_tests_1_2 的风格完全一致。
小结:让 CI 资源用在刀刃上
把本文要点浓缩为一份可直接照做的清单:
- 不要因为 "This branch is out-of-date" 而焦虑或强行 rebase——仅当基线 commit 引发测试失败或年代过久时才 rebase。
- 打
auto-submit标签等待自动合入,替代手动 merge。 - 需要紧急回滚合入问题时,走
revert标签 +reason for revert:评论,并先与 Google 团队协调。 - 遇到疑似 flake 的失败,只重跑对应测试套件,避免空 commit 触发全量重跑(一次全量 = 100~200 台 VM、30~60 分钟)。
- 新增测试目标前先评估:是否多平台必需?能否并入既有的非超时 target?必要时咨询
team-infra。 - 在
flutter/flutter中重命名/重新分片测试代价高昂:新分片先bringup: true、稳定后第二个 PR 改bringup: false、删除分片还需向每个 release candidate 分支 cherry-pick.ci.yaml——最多 4 个 PR 的协调成本。
以上所有规则的最终落地都在根目录 .ci.yaml 这份真实配置文件中,配合 docs/infra/Adding-a-new-Test-Shard.md、docs/infra/Autosubmit-bot.md 等文档阅读,即可完整理解 Flutter 这套"由 .ci.yaml 声明、由 LUCI 调度、由 bot 自动合入"的 CI 运转模型。
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