From 2608c799ebb8309384d63c9eafd7c9249d82305c Mon Sep 17 00:00:00 2001 From: Seller-1990 Date: Sat, 10 Oct 2026 18:53:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(test):=20=E8=B6=85=E6=97=B6=E9=80=80?= =?UTF-8?q?=E5=87=BA=E7=A0=81=E6=96=AD=E8=A8=80=E8=A1=A5=20124=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=20main=20=E4=B8=8A=E7=9A=84=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=BA=A2=E7=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #14 给 run_with_deadline 加了「有 GNU timeout 就优先用它」的分支,脚本这边 case 已经改成 124|137|143,但测试的断言只写了 137|143: - macOS(本机开发环境)没有 timeout,走进程组回退,返回 143 → 本地全绿; - ubuntu-latest 有 GNU timeout,到点自杀返回 124 → main 上这个用例红。 实测复现:把 GNU timeout 的行为(自成进程组 + 整组 TERM→KILL + 返回 124)做成 PATH 上的替身,再用旧断言跑,报的错与 CI 一字不差: actual: ' ⚠ 本跳到 2s 上限被强制终止(退出码 124),降级到下一个\n…' expected: /上限被强制终止(退出码 (137|143))/ error: '超时必须明说并带上退出码' 脚本的分类没错(124 确实是 GNU timeout 到点自杀的码),错的是断言把平台的 具体退出码写死了。改成只要求「是脚本认定为被强制终止的那组码」,并补注释说明 两条实现路径各返回什么。两条路径本机都实测通过(原 143 路径 28/28;注入 GNU timeout 替身后 28/28,含孙进程回收断言)。 --- test/ocr-review.test.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/ocr-review.test.js b/test/ocr-review.test.js index 9c13e8f..5675ecf 100644 --- a/test/ocr-review.test.js +++ b/test/ocr-review.test.js @@ -476,10 +476,14 @@ test('ocr-review.sh:链文件存在但零条合法条目时同样退回快照' assert.match(r.stdout, COMPLETED_RE); }); -// 单跳超时:本机 macOS + bash 3.2 没有 GNU timeout,run_with_deadline 用「自成进程组 + -// 整组 TERM」实现。这里锁住两个行为:① 挂起的一跳会被掐掉并降级;② **孙进程**也必须死 +// 单跳超时:run_with_deadline 有两条实现路径,走哪条取决于本机有没有 GNU timeout。 +// - 有 GNU timeout(CI 的 ubuntu-latest 就有):`timeout "$secs" "$@"`,到点自杀返回 124; +// - 没有(macOS + bash 3.2,本机开发环境):「自成进程组 + 整组 TERM」,返回 137/143。 +// 两条路径都必须满足同样的两个行为:① 挂起的一跳会被掐掉并降级;② **孙进程**也必须死 // (用 perl alarm 或裸 kill 只杀直接子进程,ocr 派生的子进程会漏成孤儿)。 -test('ocr-review.sh:单跳挂起会被强杀并降级(退出码 137/143 透传)', skipUnlessDeadline, () => { +// 所以断言只认「脚本判定为被强制终止」的那组退出码(脚本的 case 也是 124|137|143), +// 不锁死某一个平台的码 —— 曾经只写 137|143,结果 Linux CI 打印 124 就红了。 +test('ocr-review.sh:单跳挂起会被强杀并降级(退出码 124/137/143 透传)', skipUnlessDeadline, () => { const sb = mkSandbox(); const childPidFile = path.join(sb.root, 'grandchild.pid'); const state = path.join(sb.root, 'state'); @@ -510,7 +514,9 @@ printf '%s\\n' '${JSON.stringify(COMPLETE_JSON)}' > "$out" assert.strictEqual(r.code, 0, '第一跳超时后应降级到第二跳并成功'); // 超时提示走 stdout(与「尝试 provider=…」同一路),且必须带上真实退出码, // 否则看起来和「普通失败」一模一样,排查时会误判成 provider 坏了。 - assert.match(r.stdout, /上限被强制终止(退出码 (137|143))/, '超时必须明说并带上退出码'); + // 124 = GNU timeout 到点自杀的退出码;143/137 = 进程组被 TERM/KILL 打掉。 + // 这里只要求「是脚本认定为被强制终止的码」,不锁死平台。 + assert.match(r.stdout, /上限被强制终止(退出码 (124|137|143))/, '超时必须明说并带上退出码'); assert.match(r.stdout, /成功:fake \/ m1/, '应降级到下一跳'); // 孙进程必须已被回收(组信号生效)。给它一点时间完成 TERM→KILL。 From 11ffa79500979f6429442ff28ed4c20cb4846597 Mon Sep 17 00:00:00 2001 From: Seller-1990 Date: Sat, 10 Oct 2026 18:53:20 +0800 Subject: [PATCH 2/2] =?UTF-8?q?ci:=20PR=20=E4=B9=9F=E8=B7=91=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=88Linux=20=E5=B7=AE=E5=BC=82?= =?UTF-8?q?=E6=AD=A4=E5=89=8D=E5=8F=AA=E5=9C=A8=E5=90=88=E5=B9=B6=E5=90=8E?= =?UTF-8?q?=E6=89=8D=E6=9A=B4=E9=9C=B2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build-installers 原先只在 push main / tag / 手动触发时跑,PR 上只有 ocr-review 的 advisory 评审,不跑 npm test —— 所以「本机全绿、Linux 红」这类差异会一路 合进 main 才亮灯。PR #14 和 #15 就是这么各红了一次(两次都是同一个用例)。 构建类 job 都带 `startsWith(github.ref,'refs/tags/v') || event_name == 'workflow_dispatch'` 守卫,加 pull_request 后 PR 上只剩 test 这一个 job。 顺带修掉文件头一句不实的注释(原先写「此前只有 tag 与 PR 触发测试」,实际从 来没有过 pull_request 触发,git log 三个版本都没有)。 --- .github/workflows/build-installers.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-installers.yml b/.github/workflows/build-installers.yml index 7a22640..1c81cdb 100644 --- a/.github/workflows/build-installers.yml +++ b/.github/workflows/build-installers.yml @@ -3,12 +3,20 @@ name: build-installers # 触发: # - push tag `v*` → 构建安装包并发布到 Release # - push main → 只跑 test job(防「镜像漂移/坏改动」静默合入 main) +# - PR → main → 只跑 test job(构建类 job 有 if 条件,PR 上不会执行) # - 手动触发 → 只构建并上传 Actions artifact(用于验证) # -# 为什么加 push main:此前只有 tag 与 PR 触发测试,直接 push main 不跑任何 CI, +# 为什么加 push main:此前只有 tag 触发测试,直接 push main 不跑任何 CI, # 而本仓有「根目录 ↔ plugins/zcode-advisor/ 双副本」结构,忘同步就会把旧代码合入。 # 只跑 test(构建类 job 已加 if 条件,不在 push main 时执行):快、无网络依赖,够堵住漂移。 # +# 为什么还要加 pull_request:push main 是「事后」,红灯只在已经坏在 main 上之后才亮。 +# 2026-10 实测踩过:PR #14/#15 的 ocr-review 适配里,GNU timeout 分支在 Linux 上返回 +# 124(macOS 没有 timeout,走进程组回退返回 143),测试只断言了 137|143,于是本机全绿、 +# 合并后 main 两次变红。PR 阶段跑一遍 test,这类「只在 Linux 成立」的差异就拦在合并前。 +# 注:构建类 job 都带 `startsWith(github.ref,'refs/tags/v') || event_name == 'workflow_dispatch'` +# 守卫,PR 上只剩 test 这一个 job,代价就是一条 ubuntu 单元测试。 +# # 工具链选择依据(2026-09 查证 actions/runner-images): # - windows-2022 预装 NSIS 3.10,可直接产出 setup.exe(windows-2025 未预装 NSIS,只有 InnoSetup) # - macos-15-intel 为 Intel(x64) runner;macos-15 为 Apple Silicon(arm64) runner @@ -18,6 +26,8 @@ on: branches: [main] tags: - 'v*' + pull_request: + branches: [main] workflow_dispatch: inputs: mac_arch: