Prhub

#50318 [CI] Retry failed steps on new PR commits

原始 PR 作者 khluu 合并时间 2026-07-30 14:54 文件变更 2 提交数 3 评论 2 代码增减 +376 / -26

执行摘要

扩展 /ci retry 支持跨 commit 重试失败步骤

将现有 /ci retry 命令扩展到跨 PR commits,避免每次新 commit 都重新运行所有 CI,仅重试之前失败的步骤,节省 CI 资源和时间。

建议阅读该 PR,特别是 run_ci_command.pylist_failed_jobs 的游标分页实现和 run 函数中的新 head 分支逻辑,了解 CI 命令与流水线集成的模式(环境变量过滤、幂等性、跨构建元数据传递)。

讨论亮点

本 PR 的审查讨论较少,主要决策已在 PR body 中阐明,包括与 #49849(仓库元数据控制面)和 #50238(临时同 commit 重测试)的区分,以及对 ci-infra#442(流水线步骤键过滤)的依赖。合并者 ywang96 直接批准。

实现拆解

  1. BuildkiteClient 扩展:在 .github/workflows/scripts/run_ci_command.py 中新增 _headers_request_url 方法统一请求构建,新增 list_failed_jobs 方法通过游标分页获取失败/超时/过期作业列表,提取稳定的 step_key
  2. list_builds 修改:允许 commit 参数为 None 以查询所有构建,并支持通过元数据筛选命令触发的构建(如 github-pr-command)。
  3. run 函数新分支:如果当前 PR head 没有 Buildkite 构建,则使用 select_latest_build 查找同 PR 的最近命令触发构建,然后调用 list_failed_jobs 获取失败作业,去重后设置环境变量 VLLM_CI_ONLY_STEP_KEYS,并调用 create_retry_build_payload 创建过滤构建。同时记录源构建和 commit 到元数据,并回复 PR 评论确认两个构建。
  4. 幂等性与安全:使用已有 comment 元数据避免重复处理;在分发前重新检查 head 防止竞态;当作业无稳定 step_key 或 CI 设置失败时 fail closed 并提示使用 /ci run
  5. 测试覆盖:在 test_run_ci_command.py 中新增 5 个测试方法,验证过滤构建创建、去重、游标分页、拒绝不完整设置失败等场景,并扩展 FakeBuildkiteFakeTransport 以支持模拟。
文件 模块 状态 重要度
.github/workflows/scripts/run_ci_command.py CI 命令 modified 7.45
.github/workflows/scripts/test_run_ci_command.py CI 测试 modified 7.44

关键符号

_headers _request_url list_failed_jobs create_retry_build_payload list_builds run

关键源码片段

.github/workflows/scripts/run_ci_command.py infrastructure

核心实现,扩展 CI 命令逻辑以支持在新 commit 上重试失败步骤

# 从 Buildkite API 获取构建中失败 / 超时 / 过期作业列表,支持游标分页
def list_failed_jobs(self, build_number: int) -> list[dict[str, Any]]:
    number = urllib.parse.quote(str(build_number), safe="")
    query = [
        ("state[]", "failed"), # 只获取失败状态
        ("state[]", "timed_out"), # 以及超时状态
        ("state[]", "expired"), # 和过期状态
        ("include_retried_jobs", "false"), # 不包含已重试的作业
        ("per_page", "100"), # 每页最多 100 条
    ]
    url = f"{self.base_url}/{number}/jobs?{urllib.parse.urlencode(query)}"
    jobs: list[dict[str, Any]] = []
    while url: # 游标分页循环
        response = self._request_url(url)
        if not isinstance(response, Mapping):
            raise ApiError(None, "Buildkite API returned an invalid job list.")
        items = response.get("items")
        links = response.get("links")
        if not isinstance(items, list) or not isinstance(links, Mapping):
            raise ApiError(None, "Buildkite API returned an invalid job list.")
        jobs.extend(items) # 收集当前页作业
        next_url = links.get("next")
        if next_url is not None and not isinstance(next_url, str):
            raise ApiError(None, "Buildkite API returned an invalid pagination URL.")
        url = next_url # 指向下一页或 None
    return jobs

评论区精华

CI 重试命令扩展设计 设计

PR body 中详细讨论了与 #49849(仓库元数据控制面)和 #50238(临时同 commit 重测试)的区分,并说明了依赖 ci-infra#442 流水线过滤的决策。审查中没有进一步争论。

结论:采用环境变量过滤流水线方式,依赖 ci-infra#442 先合并;区分了更广泛的控制面方案。 · 已解决

风险与影响

  • 依赖外部仓库:ci-infra#442 是流水线侧的前置条件,若未先合并,VLLM_CI_ONLY_STEP_KEYS 环境变量将被流水线生成器忽略,导致重试运行全部步骤而非过滤步骤。
  • API 异常处理:新增的 list_failed_jobs_request_url 可能遇到 Buildkite API 返回格式异常(如无效分页 URL),当前通过 ApiError 抛出,可能导致重试失败且无直接 fallback。
  • 竞态条件:在检查 head 和实际创建构建之间,PR 可能又有新 commit,此时创建的过滤构建基于过期作业列表。PR 中通过在 dispatch 前重新检查 head 来缓解,但仍有窗口。
  • 幂等性依赖元数据:依赖 comment ID 元数据去重,若元数据丢失(如 Buildkite 清理)可能导致重复重试。

影响范围仅限于 CI 流程和开发体验。开发者可以使用 /ci retry 在新 commit 上仅重试之前失败的步骤,从而节省 CI 资源并加快反馈循环。不涉及模型推理、用户请求或系统稳定性。团队需确保 ci-infra#442 先合并,并在发布后通知贡献者新行为。

依赖外部仓库合并 幂等性依赖元数据 新代码覆盖 CI 命令路径

关联 Issue

#442 [CI] Add explicit step-key filtering to pipeline generator

完整报告

参与讨论