Flutter 使用 `led` CLI 为 PR 运行 DeviceLab 与 Post-submit 测试的完整指南
本篇技术指南围绕 Flutter 仓库中的 Agent 技能文档 .agents/skills/run-devicelab-with-led/SKILL.md 展开,讲解如何借助 LUCI 的 led 命令行工具,为某个 Pull Request 重新触发 post-submit 或 DeviceLab 测试(例如 PR 合入后看板变红、或需要在合入前对 flaky 测试多跑几轮验证)。读完本文,你将掌握 led get-builder、led edit、led edit-recipe-bundle、led launch 组成的完整命令管道的用法、每个参数的含义,以及该流程与仓库内 .ci.yaml 构建器定义、dev/bots/README.md 和 dev/devicelab/README.md 之间的对应关系。
什么场景需要手动用 led 跑 DeviceLab 测试
官方配套文档 docs/infra/Running-Devicelab-Tests-For-PR.md 给出了两类典型动机:
- PR 已合入但看板变红:pre-submit 阶段全部通过,但 post-submit(提交后)构建失败,需要手动重新触发对应测试;
- Deflake(验证测试稳定性):怀疑某个测试是 flaky 的,需要把它重复跑几轮确认,而不是直接基于一次失败下结论。
技能文档 SKILL.md 将其描述为:当你被要求 "run / launch / try / deflake a post-submit or devicelab test against a PR using led" 时使用本流程。DeviceLab 本身是 Flutter 的物理设备实验室,dev/devicelab/README.md 说明任务会按设备类型(linux_android、mac_ios、windows_android 等)分配给空闲设备,失败的任务会自动重跑,只有当某次重跑成功后该任务才报告成功,并标记 flake——这正是"手动多跑几次来 deflake"这一需求的底层机制。
前置条件(Prerequisites)
技能文档列出了三条硬性前置条件,在开始执行命令管道之前应逐条验证:
| # | 前置条件 | 验证方式 | 说明 |
|---|---|---|---|
| 1 | led 工具可用 |
执行 led auth-info |
led 来自 Google 的 depot_tools 项目,必须已安装并在 PATH 中 |
| 2 | SSO 认证有效 | 执行 gcertstatus 或 glogin status |
凭据过期时需用户执行 glogin 或 gcert 重新认证;recipes 仓库的 git 操作依赖 SSO 认证 |
| 3 | recipes 仓库 checkout(仅在用到 led edit-recipe-bundle 时必需) |
从 Flutter recipes 仓库根目录执行命令 |
用于测试本地 recipe 改动或 recipe-only 的 PR;执行 git clone https://flutter.googlesource.com/recipes 获取 |
此外,技能文档的头部注释(Host assumptions)明确要求执行环境还具备:
- 已安装且在
PATH中的工具:led(来自 depot_tools)、git、gh; - 已完成认证的:
led、gh、gcert/glogin。
同时需注意 dev/bots/README.md 中的通用基建前置:使用 Flutter 构建基础设施需要 depot_tools(led 即随其分发),并且 dev/bots/README.md#L54-L79 中"Editing a recipe"一节的典型 recipe 编辑循环第 4 步与本技能文档给出的是同一条 led 管道,两者互为印证。若 led 命令失败,该文档建议先确认 depot_tools checkout 已是最新。
工作流第一步:按 PR 类型收集输入
执行管道前需收集两个必需参数:
PR_NUMBER:目标 Pull Request 的编号(例如123456)。它将被拼进 LUCI 的 git ref,即refs/pull/PR_NUMBER/head,指向 PR 分支的最新提交;PRESUBMIT_TEST:要运行的 LUCI staging builder 的精确名称(例如Windows_mokey hot_mode_dev_cycle_win__benchmark或Mac_ios microbenchmarks_ios)。注意 builder 名称可能包含空格,命令中需要整体用引号包住。
builder 名称不是凭空指定的——它必须对应 .ci.yaml 中已定义的构建器。以文档示例中的 Windows_mokey hot_mode_dev_cycle_win__benchmark 为例,可在 .ci.yaml#L7615-L7622 中确认其定义:
# windows mokey benchmark
- name: Windows_mokey hot_mode_dev_cycle_win__benchmark
recipe: devicelab/devicelab_drone
presubmit: false
timeout: 60
properties:
tags: >
["devicelab", "android", "windows", "mokey"]
task_name: hot_mode_dev_cycle_win__benchmark
从这段配置可以看出:该构建器使用 devicelab/devicelab_drone recipe 在 Windows "mokey" 机器(带 Android 设备的物理机)上执行,presubmit: false 表明它是提交后(post-submit)而非 PR 检查阶段运行的测试,超时 60 分钟,实际执行的任务由 properties.task_name 指定。这也解释了为什么该测试属于"post-submit"类别、需要借助本流程手动针对 PR 触发。
工作流第二步:执行 led 命令管道
关于工作目录
如果本次执行包含 led edit-recipe-bundle 且目的是测试本地 recipe 修改,必须先 cd 到本地 recipes 仓库的根目录再执行管道——led edit-recipe-bundle 会把当前工作目录($CWD)打包为 recipe bundle 并随构建一起下发。如果只是想针对 PR 跑测试而没有本地 recipe 改动,led edit-recipe-bundle 这一环可以从 recipes checkout 目录运行,也可以从管道中省略。
命令管道
技能文档给出的标准管道如下(将 PRESUBMIT_TEST 与 PR_NUMBER 替换为实际值):
led get-builder 'luci.flutter.staging:PRESUBMIT_TEST' \
| led edit -pa git_ref='refs/pull/PR_NUMBER/head' \
| led edit -pa git_url='https://github.com/flutter/flutter' \
| led edit-recipe-bundle \
| led launch
各阶段作用解析:
led get-builder 'luci.flutter.staging:PRESUBMIT_TEST':从 LUCI 的luci.flutter.staging项目(bucket)中拉取指定 builder 的完整构建定义(含 recipe、properties、dependencies 等),作为后续编辑的基础。staging bucket 的意义在于:它不会污染正式主干流水线,专门用于此类针对 PR 的手动触发;led edit -pa git_ref='refs/pull/PR_NUMBER/head':-pa即"set property"(设置构建属性)。将待测代码来源覆盖为 PR 分支的最新提交。构建机上会以此 ref 检出代码;led edit -pa git_url='https://github.com/flutter/flutter':将代码来源 URL 覆盖为 framework 仓库。对 framework 的 PR 用上述地址;对 engine 仓库的 PR 则应改为 engine 对应的仓库地址(dev/bots/README.md#L73-L74 中同样以git_ref/git_url指向"intended changes to build"的表述说明了这两个属性的含义);led edit-recipe-bundle:将本地recipescheckout(当前目录)打包为 recipe bundle,使本次构建使用本地修改后的 recipe 而非 LUCI 上已发布的版本。仅在测试本地 recipe 改动时需要;led launch:提交构建到 LUCI 调度执行。
工作流第三步:回报已启动任务的信息
led launch 执行后会输出关于所启动构建的详细信息,包括 Swarming / LUCI 任务链接。正确的收尾动作是把该 URL 提供给用户,以便其跟踪执行进度、查看构建日志。对 DeviceLab 类构建而言,日志中还包含任务在物理设备上的执行过程;测试最终状态(成功 / 失败 / flake 标记)则体现在 dev/devicelab/README.md 所描述的自动重跑与结果上报机制中。
完整示例:为 framework PR 运行 Windows_mokey hot_mode_dev_cycle_win__benchmark
技能文档给出了一条"用户提示 → 具体动作"的对照示例:
- 用户提示:"Run the devicelab test
Windows_mokey hot_mode_dev_cycle_win__benchmarkfor framework PR 12345 with led." - 动作:运行以下命令(若为测试本地 recipe 改动,先切换到本地
recipes/目录):
led get-builder 'luci.flutter.staging:Windows_mokey hot_mode_dev_cycle_win__benchmark' \
| led edit -pa git_ref='refs/pull/12345/head' \
| led edit -pa git_url='https://github.com/flutter/flutter' \
| led edit-recipe-bundle \
| led launch
对照仓库证据可以完整解释这条命令:luci.flutter.staging 定位 staging 项目中的 builder;builder 名称与 .ci.yaml#L7615-L7622 中的定义逐字一致;git_ref 指向 PR 12345 的 head 提交;git_url 表明被测代码来自 framework 仓库;edit-recipe-bundle 使本次运行带上本地 recipe 改动(若无需验证 recipe 可去掉该行);launch 将构建下发到 devicelab/devicelab_drone recipe 对应的 Windows Android 物理设备执行。
适用前提与限制小结
- 本流程依赖 Google 内部基建(LUCI / Swarming / depot_tools 中的
led、SSO 认证),面向 Flutter 基础设施贡献者;普通用户无需也无法执行,dev/bots/README.md 亦说明 Flutter 构建看板的重新触发需要特殊权限(需提team-infraissue 申请); PRESUBMIT_TEST必须是 LUCI staging builder 的精确全名,可在 .ci.yaml(framework)与 engine 侧配置中查找;- 使用
led edit-recipe-bundle时工作目录必须是recipes仓库根目录,且 recipes 改动应遵循 dev/bots/README.md#L54-L79 的 recipe 编辑规范(如recipes.py test train更新期望输出、recipe 需 100% 测试覆盖); led执行失败时,优先检查depot_tools是否已更新、SSO 凭据(gcertstatus/glogin status)是否过期。
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