Prhub

#50132 [CI] Add comment-based Buildkite triggers

原始 PR 作者 khluu 合并时间 2026-07-29 09:41 文件变更 6 提交数 3 评论 1 代码增减 +989 / -5

执行摘要

引入评论命令触发 Buildkite CI

Replace label-driven full CI triggering with explicit PR comment commands so reviewers and authors can request CI only when needed.

该 PR 是 CI 流程的显著改进,安全设计细致(精确命令、权限控制、只检出默认分支),并配有完整测试。建议 CI 团队和架构师精读 run_ci_command.py 中的授权逻辑与 Buildkite 交互设计。

讨论亮点

本 PR 未收到实质性 Review 评论(仅 claude[bot] 自动回复)。但 PR body 中详细阐述了安全设计与 Rollout 要求,包括精确命令匹配、per-PR 并发、仅检出默认分支、以及秘密变量配置步骤。

实现拆解

  1. 新增工作流 run-ci-command.yml:监听 issue_comment 事件,匹配精确命令 /ci run/ci retry,设置 per-PR 并发组。
  2. 新增核心脚本 run_ci_command.py:实现 GitHub 和 Buildkite API 客户端、命令解析、权限授权、创建构建/重试失败任务、活跃构建检查等逻辑。脚本依赖 Python 标准库,无外部包。
  3. 新增测试 test_run_ci_command.py:通过 FakeGitHubFakeBuildkite 模拟网络调用,覆盖 15 个用例(精确命令匹配、权限边界、信任用户、活跃构建检测等)。
  4. 修改 CI 消息通知和 pre-commit 检查:更新 new_pr_bot.ymlpre-commit.yml 中的提示文本,标明新触发方式。
  5. 更新贡献文档 docs/contributing/README.md:说明新的 CI 触发流程。
文件 模块 状态 重要度
.github/workflows/scripts/run_ci_command.py CI 核心脚本 added 7.75
.github/workflows/scripts/test_run_ci_command.py CI 测试 added 7.48
.github/workflows/run-ci-command.yml 工作流定义 added 5.07

关键符号

authorize has_trusted_approval parse_command create_build_payload select_latest_build is_active_build is_build_for_pr run BuildkiteClient.list_builds BuildkiteClient.create_build BuildkiteClient.retry_failed_jobs

关键源码片段

.github/workflows/scripts/run_ci_command.py infrastructure

核心命令处理脚本,实现 GitHub API 交互、Buildkite 构建创建、权限检查等所有逻辑。

class GitHubClient:
    def __init__(
        self,
        token: str,
        repository: str,
        transport: HttpTransport | None = None,
    ) -> None:
        if not token:
            raise RuntimeError("GH_TOKEN is not set.")
        self.owner, self.repo = repository.split("/", maxsplit=1)
        self.transport = transport or HttpTransport()
        # 固定请求头,符合 GitHub API 规范
        self.headers = {
            "Accept": "application/vnd.github+json",
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
            "User-Agent": "vllm-ci-command",
            "X-GitHub-Api-Version": "2022-11-28",
        }
​
    def authorize(
        self,
        actor: str,
        *,
        pr: dict[str, Any],
        trusted_users: set[str],
    ) -> tuple[bool, str]:
        permission = self.get_permission(actor)
        if permission in TRUSTED_PERMISSIONS:
            return True, f"permission: {permission}"
        if actor == pr["user"]["login"]:
            # 作者需要批准或 ready 标签
            labels = {label["name"] for label in pr.get("labels", [])}
            if READY_LABELS & labels:
                return True, "author with ready label"
            decision = self.get_review_decision(pr["number"])
            if decision in ("APPROVED", "CHANGES_REQUESTED"):
                return True, "author with approved PR"
            return False, "author without approval or ready label"
        if actor in trusted_users:
            return True, "trusted user"
        return False, "no valid permission"
.github/workflows/scripts/test_run_ci_command.py test-coverage

完整的测试套件,15 个测试用例覆盖命令解析、权限授权、信任用户、活跃构建检测等场景,使用 fake 对象保证测试独立。

def make_pr(**overrides: Any) -> dict[str, Any]:
    # 构造默认 PR 对象,可通过 overrides 覆写
    pr = {
        "base": {"ref": "main"},
        "draft": False,
        "head": {
            "ref": "feature",
            "repo": {"clone_url": "https://github.com/contributor/vllm.git"},
            "sha": "0123456789abcdef",
        },
        "labels": [],
        "number": 42,
        "state": "open",
        "user": {"login": "author"},
    }
    pr.update(overrides)
    return pr
​
​
def make_event(command: str, actor: str = "reviewer") -> dict[str, Any]:
    # 构造 issue_comment 事件负载
    return {
        "comment": {
            "body": command,
            "id": 99,
            "user": {"login": actor},
        },
        "issue": {
            "number": 42,
            "pull_request": {},
        },
    }
​
​
class FakeGitHub:
    # 模拟 GitHub API 客户端,所有方法直接返回预设数据
    def __init__(self, *, permission="write", permissions=None, pr=None,
                 review_decision="REVIEW_REQUIRED", reviews=None):
        self.comments = []
        self.permission = permission
        self.permissions = permissions or {}
        self.pr = pr or make_pr()
        self.reactions = []
        self.review_decision = review_decision
        self.reviews = reviews or []
​
    def get_pr(self, number):
        return self.pr
​
    def get_permission(self, actor):
        return self.permissions.get(actor, self.permission)
​
    def get_review_decision(self, number):
        return self.review_decision
​
    def list_reviews(self, number):
        return self.reviews

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 依赖外部 Secret:若 BUILDKITE_API_TOKEN 未配置,工作流将失败。需手动设置且 Token 需定期轮换。
  2. 权限模型复杂性:授权逻辑依赖 GitHub 的权限和 Review 决策,若 Review 状态判断有误可能导致误触发或拒绝。
  3. HTTP 客户端健壮性:使用标准 urllib 代替 requests,缺少重试机制和连接池,可能在高延迟或失败时不够稳健。
  4. 并发控制concurrency 设置 cancel-in-progress: false 可能让同一 PR 的多个命令堆积,但脚本中的活跃构建检查会阻止重复。

影响范围:面向所有贡献者和维护者,改变了 CI 触发流程。
用户影响:贡献者需要学习 /ci run 命令,不再依赖标签触发。
系统影响:能够降低不必要的 CI 运行量,节约计算资源。
团队影响:CI 维护者需配置 Secret 并监控新工作流运行情况。

依赖外部 API Token 权限模型精细但复杂 缺少 HTTP 重试

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论