首页
/ Flutter 使用 `led` CLI 为 PR 运行 DeviceLab 与 Post-submit 测试的完整指南

Flutter 使用 `led` CLI 为 PR 运行 DeviceLab 与 Post-submit 测试的完整指南

2026-09-06 19:20:00作者:齐添朝

本篇技术指南围绕 Flutter 仓库中的 Agent 技能文档 .agents/skills/run-devicelab-with-led/SKILL.md 展开,讲解如何借助 LUCI 的 led 命令行工具,为某个 Pull Request 重新触发 post-submit 或 DeviceLab 测试(例如 PR 合入后看板变红、或需要在合入前对 flaky 测试多跑几轮验证)。读完本文,你将掌握 led get-builderled editled edit-recipe-bundleled launch 组成的完整命令管道的用法、每个参数的含义,以及该流程与仓库内 .ci.yaml 构建器定义、dev/bots/README.mddev/devicelab/README.md 之间的对应关系。

什么场景需要手动用 led 跑 DeviceLab 测试

官方配套文档 docs/infra/Running-Devicelab-Tests-For-PR.md 给出了两类典型动机:

  1. PR 已合入但看板变红:pre-submit 阶段全部通过,但 post-submit(提交后)构建失败,需要手动重新触发对应测试;
  2. 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_androidmac_ioswindows_android 等)分配给空闲设备,失败的任务会自动重跑,只有当某次重跑成功后该任务才报告成功,并标记 flake——这正是"手动多跑几次来 deflake"这一需求的底层机制。

前置条件(Prerequisites)

技能文档列出了三条硬性前置条件,在开始执行命令管道之前应逐条验证:

# 前置条件 验证方式 说明
1 led 工具可用 执行 led auth-info led 来自 Google 的 depot_tools 项目,必须已安装并在 PATH
2 SSO 认证有效 执行 gcertstatusglogin status 凭据过期时需用户执行 glogingcert 重新认证;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)、gitgh
  • 已完成认证的:ledghgcert / glogin

同时需注意 dev/bots/README.md 中的通用基建前置:使用 Flutter 构建基础设施需要 depot_toolsled 即随其分发),并且 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__benchmarkMac_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_TESTPR_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

各阶段作用解析:

  1. led get-builder 'luci.flutter.staging:PRESUBMIT_TEST':从 LUCI 的 luci.flutter.staging 项目(bucket)中拉取指定 builder 的完整构建定义(含 recipe、properties、dependencies 等),作为后续编辑的基础。staging bucket 的意义在于:它不会污染正式主干流水线,专门用于此类针对 PR 的手动触发;
  2. led edit -pa git_ref='refs/pull/PR_NUMBER/head'-pa 即"set property"(设置构建属性)。将待测代码来源覆盖为 PR 分支的最新提交。构建机上会以此 ref 检出代码;
  3. 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"的表述说明了这两个属性的含义);
  4. led edit-recipe-bundle:将本地 recipes checkout(当前目录)打包为 recipe bundle,使本次构建使用本地修改后的 recipe 而非 LUCI 上已发布的版本。仅在测试本地 recipe 改动时需要
  5. 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__benchmark for 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-infra issue 申请);
  • 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)是否过期。
登录后查看全文
热门项目推荐
相关项目推荐