Repository navigation
ci: 按 Cargo 依赖图判定平台范围,砍掉重复跑与独立胶囊 job - #1279
Conversation
一次 PR 要拉起 12 台 VM,且 feature 分支的 push 和它的 PR 各跑一遍完整矩阵。
实测最近 42 次 CI run:平均每次 86 VM-min、峰值 12 台并发;42 次里 8 次是
同一 commit 的重复 push run。
1. push 只在默认分支跑(main/beta)。feature 分支由它的 PR 覆盖;默认分支的
push run 仍保留,因为它是基线证据、也是 Rust/Playwright 缓存的唯一写入方。
文档本来就写着「push → main/beta」,实现是 ['**'],这次对齐。
2. 平台范围判定从手写路径表换成 Cargo 自己的包图:
scripts/ci-impact.mjs 用 cargo metadata --no-deps --offline 读出每个 workspace
包的目录、仓内依赖边和边上的 cfg(...) 守卫,从各平台根包走闭包 —— 文件在范围
内当且仅当拥有它的包在该闭包里。于是:
- iOS 工程层(ios-device)只看 openless-all/ios/**:共享 crate 已由 iOS 的
test job 编译校验,device-build(13min × 10x 计价的 macOS runner)不再被
crates/openless-core 的改动无谓拉起;
- ArkTS 只跑鸿蒙;linux-egui 只跑 Linux;纯前端 bundle 改动不再跑 Linux 构建
(Linux 宿主是原生 egui,不读 dist/)也不再跑 Android cargo check;
- 契约测试(*.test.mjs)只跑桌面两个 runner,不触发 Playwright 动效套件;
- 新增 frontend / android / ios-device 三个区域,判定全部 fail-open。
结果:src-tauri 改动 → 桌面+Android;core 改动 → 五个平台(真的都依赖它);
纯 TSX → 桌面+鸿蒙+动效,Linux/iOS 跳过。
3. ui-motion(15.5min)改由 frontend 区域门控。
4. native-capsule 的 macOS/Windows 双 job(payload 只有 ~40s 的 cargo check)
并入 cross-platform:那个 job 本来就为 generate_context! 构建同一个 app
workspace target 目录,增量编译几乎免费,省掉两个 VM 分配和一个缓存前缀。
契约测试同步:ci-changed-areas 用真实 git diff 双向断言新区域;
ci-cache-sharing 断言胶囊命令在 cross-platform 内且不再有独立缓存键;
workflow-concurrency 断言 push 范围;docs/ci-trigger-and-cache-policy 更新判定表。
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
跳过某平台时,事后要能一眼分清「改动够不到」和「读不到差异」。两处 detect 步 骤现在都把 `<area>=<true|false>` 打到日志,同时仍写进 GITHUB_OUTPUT。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 161fedd525
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // a native egui UI and does not consume `dist/` at all, and the Android job | ||
| // only compiles the Rust crate (`npm run build` there just satisfies | ||
| // `generate_context!`), so neither is in scope for a bundle-only change. | ||
| for (const tag of ['frontend', 'macos', 'windows', 'harmony']) tags.add(tag); |
There was a problem hiding this comment.
Include native consumers when classifying shared assets
For changes confined to app/assets/** or app/public/**, this classification skips Linux and Android even when those files are native build inputs. For example, crates/openless-core/src/vocabulary.rs:327 embeds assets/vocab-presets.json, while linux-egui/src/remote_input.rs:212–217 embeds the remote-input assets and linux-egui/src/tray.rs:354 embeds public/AppIcon.png. Calling decide with these paths confirms that Linux and Android return false. These resource changes can therefore alter native behavior or break native compilation without running the affected checks. Add explicit native-consumer reachability for these resources rather than treating the entire directories as bundle-only.
Useful? React with 👍 / 👎.
| device-build: | ||
| needs: test | ||
| needs: [changes, test] | ||
| if: needs.changes.outputs.device != 'false' |
There was a problem hiding this comment.
Preserve iOS-target compilation for shared Rust changes
For a PR changing only shared Core sources, the new gate skips the only job that compiles them for an iOS target. The test job's Cargo commands at lines 70–72 have no --target, so they compile for the macOS runner; --all-targets selects Cargo targets, not iOS architectures. This leaves existing iOS-only branches, such as the capability implementation in openless-core/src/shared_types.rs, unchecked. Previously, device-build invoked scripts/build-core.sh device, which builds for aarch64-apple-ios. Keep shared Rust changes in this gate, or add an iOS-target compile check to test before excluding them.
Useful? React with 👍 / 👎.
run 40510306688 的真实 step 计时: Windows checks 里三步 capsule = 43 + 53 + 21 = 117 s(关键路径 job,直接 +2 分钟墙钟) macOS checks 里三步 capsule = 40 + 41 + 6 = 87 s 独立 job 原本平均 1.4 / 0.7 分钟——它有专属 warm cache,且只编 capsule 自己。 合并省了 2 台 VM,却把 2 分钟加到最慢的 job 上,加权分钟反而更贵。 改成:native-capsule job 保持原样,新增 `capsule` 区域,只有拥有改动文件的包是 openless-capsule / openless-native-capsule 时才跑。常见 PR 因此少 2 台 VM 且不增加 任何墙钟时间;胶囊相关 PR 与今天一致。 契约测试同步:胶囊 job 必须带自己的区域门控;ci-changed-areas 场景新增 capsule 列。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 56763b82aa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| macos: ['openless', 'openless-native-capsule'], | ||
| windows: ['openless', 'openless-native-capsule'], | ||
| android: ['openless'], | ||
| linux: ['openless-linux-egui'], |
There was a problem hiding this comment.
Include the Harmony host in the Linux test gate
For a PR changing only openless-all/app/harmony/rust/**, this root set makes decide('linux', ...) return false, skipping the workflow that actually runs the Harmony Rust unit tests. In the inspected .github/workflows/check-linux-egui.yml, line 120 runs cargo test --locked -p openless-harmony-host under the Linux scope gate; .github/workflows/ci.yml's harmony-check builds the HAP but does not run those tests. Consequently, changes to the host's credentials, voice configuration, or tests can merge without executing its Rust tests. Include openless-harmony-host in the Linux check's roots, or move its test command into the Harmony-gated job.
Useful? React with 👍 / 👎.
失败快照给出了确定答案:controlledXs 是空数组(Expected length 3 / Received length 0 / Received array []),而同一 case 前面几条断言都过了——开关确实动了, 只是采样钩子没挂上。 原因:flip() 用 control.evaluate() 派发采样函数(在里面 addEventListener 'transitionrun'),但没有 await 就发 mouse.down()/up()。evaluate 和 click 是两条 独立的协议消息,CI 上负载高时 click 先到页面,监听还没挂上 → 抓不到 3 个受控 采样点。这就是最近 43 次 CI run 里失败 6 次(14%)、落在 5 个互不相关分支上 (含 beta 自身)的那条 `语音编辑 exposed its transform transition`。 改法(不放宽断言): - 把「挂监听」拆成一次 await 的 evaluate,采样结果存进页面侧 WeakMap(按控件 元素键),确认挂好后才点击; - 采样循环仍在点击时启动、750ms 后收尾,所以 xs.at(-1) 依然是静止位置; - 三条断言(状态翻转、滑块位移、transform 过渡跑完、3 个受控中间位置)全部 保持原样,重挂载仍会因为 xs 为 NaN 而失败。 本地验证:webkit project 全文件 --repeat-each=6 → 30/30 通过(8.7 分钟), 即那条 flaky case 连跑 6 次全绿。tsc --noEmit 干净。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1984ab703
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /^openless-all\/app\/(index\.html|package\.json|package-lock\.json|vite\.config\.[^/]+|tailwind\.[^/]+|postcss\.[^/]+|tsconfig[^/]*\.json)$/.test( | ||
| file, | ||
| ) || |
There was a problem hiding this comment.
Include npm manifests in the Android check's inputs
For PRs changing only package.json or package-lock.json, this classification makes decide('android', ...) return false, even though these files control Android build tooling, not just the rendered bundle. In .github/workflows/ci.yml, android-check installs the locked dependencies with npm ci and then uses the installed Tauri CLI to generate the Android project at lines 225–226 before compiling and testing its Gradle scaffolding. Consequently, a Tauri CLI dependency update skips the Android-specific validation of the project it generates. Classify npm manifests as Android tooling inputs rather than bundle-only changes.
Useful? React with 👍 / 👎.
上一版修掉了监听竞态,但采样循环仍在点击前就开始了 45 帧(~750ms); 如果 CI 把 click 延迟到 800ms,end 会取到动画前的位置。改成 挂监听(await)→ 点击 → 采样循环,45 帧仍覆盖 180ms 的滑动并收尾, xs.at(-1) 恒为静止位置。 本地 webkit:全文件 5/5 通过;含 --repeat-each=4 的 20/20。
摘要
Fixes #(无关联 issue:来自「CI 需要太多虚拟机」的排查)。
先量化,再动手。最近 42 次 CI run + 8 次 iOS run 的 job 级数据(GitHub 计价倍率 ubuntu 1x / windows 2x / macOS 10x):
一次 run 峰值 12 台 VM,免费额度 20 并发;而
push: ['**']让 feature 分支的 push 与它的 PR 各跑一遍完整矩阵——42 次 run 里 8 次是这种重复(例如40497478537push /40497484491同分支 PR)。修复 / 新增 / 改进
push 只在默认分支跑(
[main, beta])。feature 分支由 PR 覆盖;默认分支的 push run 仍是基线证据、也是 Rust/Playwright 缓存的唯一写入方(save-if本来就只看 beta)。文档 §1 表格本来就写「push → main/beta」,是实现与文档不一致,这次对齐 → 去掉约 20% 的总计算。平台范围判定改为走 Cargo 自己的包图(
scripts/ci-impact.mjs,由ci-changed-areas.sh调用):cargo metadata --no-deps --offline给出每个 workspace 包的目录、仓内依赖边、以及每条边上的cfg(...)守卫(例如openless → openless-capsule带cfg(not(any(target_os = "android", target_os = "ios"))))。从各平台根包走闭包,一个文件在范围内当且仅当拥有它的包在该闭包里;非 Rust 树(bundle / ArkTS / Xcode / 工具链)由显式规则覆盖,未覆盖即 fail-open。不再靠手写 glob 猜。实测判定矩阵(
ci-changed-areas.sh <area> <base>,真实 git diff):src-tauri/src/lib.rscrates/openless-core/src/lib.rsnative-capsule/src/lib.rslinux-egui/src/…harmony/…/*.etssrc/pages/X.tsxios/**docs/*.md*/Cargo.lock、.github/**(另有
capsule区域,控制独立的胶囊 job:只有openless-capsule/openless-native-capsule里的改动才为它拉起 runner。)iOS
device-build独立门控(ios-device区域)。共享 crate 已经由 iOS 的testjob 编译 + clippy 校验;device-build只证明 Xcode/Rime 工程层能构建,因此只跟openless-all/ios/**。这是单 job 最贵的一项(13 min × 10x),此前被crates/openless-core/**的任意改动拉起。ui-motion由新frontend区域门控:只跟浏览器会渲染的面(src/**、public/**、e2e/**、assets/**、样式与 vite/playwright 配置)。契约套件(*.test.mjs)只跑桌面两个 runner,不再拉起 15.5 分钟的 Playwright。android-check独立门控:bundle-only 改动不跑(该 job 只编 Rust,npm run build只为满足generate_context!)。native-capsule job 按新
capsule区域门控。(先说一个被实测否决的方案:把它们并进cross-platform。)合并的原始估算——「那两个 job 只有 ~40 s 的载荷,而
cross-platform本来就在同一 workspace 的 target 目录上,增量编译几乎免费」——是错的。本 PR 第一次跑出来后按 step 计时:cargo check -p openless-native-capsulecargo test -p openless-capsule --features ui,host-D warnings独立 job 原本只要 0.7 min(macOS)/ 1.4 min(Windows):它有专属的
native-capsulewarm cache,而且只编 capsule 自己。合并省下 2 台 VM,却把 2 分钟加到了最慢的 job(Windows checks)上,加权分钟反而更贵。所以改成保留独立 job + 新增
capsule区域:只有当改动文件的所属包是openless-capsule/openless-native-capsule时才跑。常见 PR 因此少 2 台 VM 且不增加任何墙钟时间;胶囊相关 PR 与今天完全一致。兼容
Windows checks/macOS checks的步骤顺序、缓存键、-D warnings全部不变。workflow_dispatch仍是全量。Native capsule (…)这个 check 名消失不会卡住任何已开 PR。true(运行)。契约测试对两个方向都断言。测试计划
node scripts/ci-changed-areas.test.mjs(真实 git 仓库 + 真实cargo metadata,双向断言 7 个区域,含「无基线 / 空 diff / 未知 revision / 未知区域」回退)node scripts/ci-cache-sharing.test.mjs && node scripts/workflow-concurrency-contract.test.mjscross-platform内、无native-capsule缓存键;push 范围断言)for t in scripts/*.test.mjs; do node "$t"; donenpx prettier --check改动文件 +bash -n scripts/ci-changed-areas.shcargo metadata --no-deps --offline在空CARGO_HOME下跑(模拟干净 runner)changesjob 不需要装 Rustgh api .../runs/<id>/jobs汇总Changed areas在干净 runner 上 11 s 完成,cargo metadata --offline无需 registry 缓存;判定结果已打进 job 日志(tauri=true/frontend=false…),事后能一眼分清「够不到」与「读不到差异」顺手修掉一个把 UI 改动拖下水的 flake
UI motion里的e2e/switch-motion.spec.ts在最近 43 次 CI run 里失败 6 次(14%),落在 5 个互不相关的分支上,包括beta自己的40481488914—— 也就是说改一点 UI 就会随机报错,而报告的信息(语音编辑 exposed its transform transition)看起来像是在指责产品代码。失败快照把它钉死了:
Expected length: 3 / Received length: 0 / Received array: [],而同 case 前面几条断言(状态翻转、滑块位移、transition 跑完)全部通过。开关每次都真的动了,只是采样钩子没挂上。根因是测试自己的竞态:
flip()用control.evaluate()派发采样函数并在里面addEventListener('transitionrun'),但没有 await 就发mouse.down()/up()。两条独立协议消息,CI 负载高时 click 先到页面。修法(不放宽任何断言):把"挂监听"拆成一次
await的 evaluate,结果存页面侧 WeakMap,确认挂好后才点击;采样循环仍随点击启动、750ms 收尾,xs.at(-1)仍是静止位置;重挂载仍会因为xs为 NaN 而失败。本地 webkit--repeat-each=6跑满 30/30 通过(8.7 分钟),那条 flaky case 连跑 6 次全绿。没有加
retries:根因修掉了,就不该用重试掩盖信号。