From 11ae2a2c3b8e23be1e64fca7f9ef25dbcb1929a0 Mon Sep 17 00:00:00 2001 From: xile611 Date: Thu, 17 Sep 2026 16:08:36 +0800 Subject: [PATCH] ci: route Bug Server builds by PR source --- .github/workflows/bug-server-pr-bundle.yml | 1 + .github/workflows/bug-server.yml | 7 ++++--- .../plans/2026-09-17-bug-server-dispatch.md | 17 +++++++++++++++- .../2026-09-17-bug-server-dispatch-design.md | 12 +++++++++-- tools/bugserver-trigger/README.md | 20 ++++++++++++++++--- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/.github/workflows/bug-server-pr-bundle.yml b/.github/workflows/bug-server-pr-bundle.yml index cad561b752..06de6c1986 100644 --- a/.github/workflows/bug-server-pr-bundle.yml +++ b/.github/workflows/bug-server-pr-bundle.yml @@ -9,6 +9,7 @@ permissions: jobs: build-pr-bundle: + if: github.event.pull_request.head.repo.full_name != github.repository runs-on: macos-latest timeout-minutes: 30 steps: diff --git a/.github/workflows/bug-server.yml b/.github/workflows/bug-server.yml index 94636f9172..fa2815ccf5 100644 --- a/.github/workflows/bug-server.yml +++ b/.github/workflows/bug-server.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: inputs: pr_number: - description: '待验证的 PR 编号(支持外部 fork PR)' + description: '待验证的外部 fork PR 编号' required: true type: string head_sha: @@ -22,9 +22,10 @@ permissions: jobs: build: - if: github.event_name != 'workflow_dispatch' + if: >- + github.event_name == 'push' || + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) runs-on: macos-latest - strategy: matrix: node-version: [18.x] diff --git a/docs/superpowers/plans/2026-09-17-bug-server-dispatch.md b/docs/superpowers/plans/2026-09-17-bug-server-dispatch.md index 6d9f501c66..79dab6f1e4 100644 --- a/docs/superpowers/plans/2026-09-17-bug-server-dispatch.md +++ b/docs/superpowers/plans/2026-09-17-bug-server-dispatch.md @@ -41,7 +41,16 @@ - [x] 从最终 workflow 提取下载、ZIP 读取、提交命令,在隔离目录模拟 artifact 到客户端的完整数据流,确认产物仅作为字节上传。 - [x] 检查格式及最终 diff,记录本地验证和线上未验证项。 -## 验证结果 +## 任务 4:按 PR 来源分流 + +文件:`.github/workflows/bug-server.yml`、`.github/workflows/bug-server-pr-bundle.yml`、`tools/bugserver-trigger/README.md`。 + +- [x] 将 CI 的 `build.if` 改为 `github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)`,保留两个手动 job 的条件。 +- [x] 为 `build-pr-bundle` 添加 `if: github.event.pull_request.head.repo.full_name != github.repository`。 +- [x] 中文说明明确仓库内 PR 自动测试、fork PR 构建后手动测试、另一构建 job 显示 skipped,以及仓库内 PR 应重跑 CI。 +- [x] 从 YAML 读取实际条件,验证仓库内 PR、fork PR、`main` push、手动触发的执行矩阵;使用 actionlint 校验两个 workflow,运行现有 Node/Python 测试和 `git diff --check`。 + +## 原手动入口验证结果 - Node 来源校验测试 36 项、Python ZIP 测试 6 项全部通过。 - 本地 macOS / Node.js 24.19.0 使用现有依赖完成四步构建,生成约 5.1 MB 的 bundle,`node --check` 通过。CI 构建沿用 Node.js 18;本地未重新安装全部 Rush 依赖。构建保留已有 sourcemap、ES module this 和循环依赖告警。 @@ -51,3 +60,9 @@ - 读取所得的抛错 JavaScript bundle 交给隔离客户端 mock,成功、图片失败、SCM 失败、token 缺失四种场景通过;PR 元数据与 API 顺序保持正确,bundle 未执行。 - resolver 和 ZIP 读取器与 VRender 最新实现相同;仅 workflow 的 runner、构建步骤、依赖版本、超时及中文说明保留 VChart 适配。 - 本次未推送或运行 VChart 线上 Actions/CodeQL,也未调用真实 Bug Server。VRender 的线上验证记录不能替代 VChart 自身的线上验收。 + +## PR 来源分流验证结果 + +- 仓库内 PR、外部 fork PR、同组织不同仓库 PR、`main` push、手动触发共 5 种场景的实际条件求值通过;每个 PR 只执行一个构建 job,手动提交仍依赖校验 job。 +- actionlint 1.7.12、现有 Node 测试 36 项、Python 测试 6 项和 `git diff --check` 通过。 +- 构建步骤及提交脚本未变,本次未重跑完整构建,未触发线上 Actions 或 Bug Server。 diff --git a/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md b/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md index 439817cca9..475c8111bf 100644 --- a/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md +++ b/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md @@ -8,13 +8,21 @@ ## 数据流 -1. `bug-server-pr-bundle.yml` 仅由 `pull_request` 触发,检出准确 head SHA,沿用 VChart 的 macOS、Node.js 18 和 Rush 构建顺序。关闭持久 checkout 凭据、不传入 Bug Server token,缓存写入属于 PR 作用域。上传 `bug-server-pr-<编号>-`,保留 7 天。 +1. `bug-server-pr-bundle.yml` 仅在来自外部 fork 的 `pull_request` 中执行构建,检出准确 head SHA,沿用 VChart 的 macOS、Node.js 18 和 Rush 构建顺序。关闭持久 checkout 凭据、不传入 Bug Server token,缓存写入属于 PR 作用域。上传 `bug-server-pr-<编号>-`,保留 7 天。 2. 仅从仓库默认分支运行 `workflow_dispatch`,从固定的 `github.workflow_sha` 加载校验脚本。校验 PR 编号、40 位十六进制 SHA、PR 所属仓库和当前 head。 3. 通过 workflow ID/路径、PR 事件、成功状态、base/head 仓库 ID、源分支及 run SHA 绑定来源构建。fork 的运行记录可能没有 PR 列表;如列表存在则必须包含目标 PR。 4. 选择最新匹配运行中唯一、未过期的命名产物,复核 artifact API 的 run ID、仓库 ID、SHA。不回退到较旧运行;失败时要求先成功运行 PR bundle workflow。 5. 提交 runner 从 workflow SHA 加载可信脚本,按 artifact ID 下载 ZIP,只接受一个普通 `index.js` 文件,最大 64 MiB。只读取文件字节到固定目标,禁止路径解压、覆盖已有文件或执行产物。 6. 固定版本客户端依赖独立安装且禁用 lifecycle scripts。仅触发 API 的 step 接收 `BUG_SERVER_TOKEN`,传入被测 SHA、PR ref、源分支,保留图片测试和性能测试行为。 -7. 两个 workflow 默认 `contents: read`;查询/下载 job 需要 `actions: read`,目标校验另需 `pull-requests: read`。summary 记录 PR、SHA 和来源构建,日志保留用例结果。既有 push / pull_request 自动构建与测试行为保留。 +7. 两个 workflow 默认 `contents: read`;查询/下载 job 需要 `actions: read`,目标校验另需 `pull-requests: read`。summary 记录 PR、SHA 和来源构建,日志保留用例结果。`main` push 和仓库内 PR 继续自动构建与测试。 + +## 按 PR 来源分流 + +PR 目标分支仍为 `main`、`develop`、`dev/**`。比较 `github.event.pull_request.head.repo.full_name` 与 `github.repository`:相等时只执行 **Bug Server CI** 的 `build` job,不相等时只执行 **Bug Server PR Bundle** 的 `build-pr-bundle` job。每个 PR 事件只实际构建一次。 + +GitHub 的 PR 事件过滤器不能按来源仓库过滤,因此使用 job 级 `if`;另一个 workflow 仍可生成运行记录,但其构建 job 会跳过,不分配 runner。`main` push 继续执行 CI 构建,`workflow_dispatch` 仅执行既有校验和提交 job。仓库内 PR 重跑 CI 即可;fork PR 先构建 bundle,再由维护者手动提交测试。 + +验证覆盖仓库内 PR、fork PR、`main` push 和手动触发四种事件,检查构建互斥及手动 job 的条件;运行 actionlint 和现有来源、ZIP 校验测试。构建命令不变,无需重复完整构建。 ## 维护者操作 diff --git a/tools/bugserver-trigger/README.md b/tools/bugserver-trigger/README.md index 7035045ca1..d2ad880f73 100644 --- a/tools/bugserver-trigger/README.md +++ b/tools/bugserver-trigger/README.md @@ -2,11 +2,25 @@ `scripts/trigger-test.ts` 上传 `dist/index.js`,等待 SCM 构建,运行并等待图片测试,然后触发性能测试。调用接口需要仓库 secret `BUG_SERVER_TOKEN`。 +## 自动执行规则 + +目标分支为 `main`、`develop`、`dev/**` 的 PR,按来源仓库分流,每个 PR 事件只实际构建一次: + +| 事件 | Bug Server CI | Bug Server PR Bundle | +| -------------- | ------------------ | -------------------------------------- | +| 仓库内 PR | 构建并提交测试 | 跳过构建 | +| 外部 fork PR | 跳过自动构建与测试 | 构建并保存产物,等待维护者手动提交测试 | +| push 到 `main` | 构建并提交测试 | 不触发 | + +来源判断使用 PR head 仓库的完整名称与当前仓库名称比较。GitHub 的 `pull_request` 触发器不能按来源仓库过滤,因此在 job 级设置互斥条件;两个 workflow 仍可能出现运行记录,但另一构建 job 显示 `skipped`,不会启动 runner。 + +仓库内 PR 如需重新验证,重跑 **Bug Server CI**。下面的手动入口用于外部 fork PR,依赖 **Bug Server PR Bundle** 产物;仓库内 PR 不再生成该产物。 + ## 手动验证外部 PR 两个 workflow 合入仓库默认分支 `develop` 后,先等待该 PR head 的 **Bug Server PR Bundle** 构建成功。外部 fork 的运行可能需要维护者先批准。随后拥有仓库写权限的维护者可打开 **Actions → Bug Server CI → Run workflow**,选择 **develop** 并填写: -- `pr_number`:VChart 仓库的 PR 编号,支持外部 fork PR。 +- `pr_number`:提交到 VChart 仓库的外部 fork PR 编号。 - `head_sha`:已审查的 PR head 的完整 40 位 SHA。 也可使用 GitHub CLI(替换下面的 PR 编号和 SHA): @@ -34,13 +48,13 @@ gh workflow run bug-server.yml \ ## 执行边界 -1. **Bug Server PR Bundle** 仅由 `pull_request` 触发,在 macOS runner 上检出准确 head SHA,使用 Node.js 18 依次构建 vutils-extension、vchart、vchart-extension 和 bugserver-trigger。不持有 Bug Server token、不保留 checkout 凭据,缓存写入仅属于 PR 作用域;产物名为 `bug-server-pr-<编号>-`。 +1. **Bug Server PR Bundle** 仅在来自外部 fork 的 `pull_request` 中执行构建,在 macOS runner 上检出准确 head SHA,使用 Node.js 18 依次构建 vutils-extension、vchart、vchart-extension 和 bugserver-trigger。不持有 Bug Server token、不保留 checkout 凭据,缓存写入仅属于 PR 作用域;产物名为 `bug-server-pr-<编号>-`。 2. 手动校验 job 从默认分支 workflow 的固定提交加载脚本,通过 GitHub API 确认当前 PR head,并校验来源 workflow ID/路径、PR 事件、成功状态、base/head 仓库 ID、源分支和 run SHA。选择最新匹配运行中唯一且未过期的命名产物,再复核产物的运行 ID、仓库 ID 和 SHA。fork 的运行记录可能没有 PR 列表,此时由仓库、分支、SHA 绑定来源;如列表存在,则还必须包含目标 PR。 3. 提交 job 使用 workflow 固定提交中的可信脚本,按 artifact ID 下载 ZIP,只接受单一普通 `index.js` 文件,最大 64 MiB。读取器只把字节写入固定路径,不按 ZIP 路径解压。客户端依赖以 `--ignore-scripts` 独立安装;bundle 只作为文件上传,此 runner 不执行 PR bundle 或其 package scripts。 两个 workflow 默认 `contents: read`;手动查询和下载 job 另需 `actions: read`,目标校验还需 `pull-requests: read`。token 仅注入最后调用 Bug Server API 的 step。默认分支手动流程不检出或构建 PR 代码;仅省略缓存步骤无法消除默认分支缓存写权限,因此采用 PR 事件隔离构建。 -已有 push / pull_request 自动入口保留。外部 fork 的自动运行仍然无法获得仓库 secret,使用上述手动入口完成验证。 +`main` push 和仓库内 PR 保留自动构建与测试。外部 fork PR 的自动运行只构建产物,不调用需要仓库 secret 的 Bug Server API,使用上述手动入口完成验证。 ## 本地检查