Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/build-installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,6 +26,8 @@ on:
branches: [main]
tags:
- 'v*'
pull_request:
branches: [main]
workflow_dispatch:
inputs:
mac_arch:
Expand Down
14 changes: 10 additions & 4 deletions test/ocr-review.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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。
Expand Down
Loading