Repository navigation
完善egui相关内容 - #1117
Merged
Merged
完善egui相关内容#1117
Conversation
Beta.3 merged as 913279c; this revision carries the review fixes (fcitx5 startup barrier, UI bridge protocol v2, field-patch settings saves, history cache) into the deb/rpm channel.
Lists the observable behaviour each review fix introduced: the fcitx5 startup barrier and its failure/recovery path, the UI bridge handshake and reconnect, field-patch settings saves under conflict, history delete identity and caching, plus the previous round's parity regressions and the install/upgrade checks.
…ged daemon The barrier required the `org.fcitx.Fcitx5` owner name to change after a reload, but `restart_fcitx5_via_dbus` reported success whenever the D-Bus call was dispatched. A compositor-launched fcitx5 (`fcitx5-wayland-launcher --reopen`) sets `CanRestart` false and then silently ignores `Restart`, so the owner never changed: every start burned the full 15 second budget and died in the startup error window even though the running addon answered and hotkey registration would have worked. - Readiness is now the addon interface, not a replaced daemon. A reload that the daemon does not perform is a warning (`reload_wait`, `ReloadPlan`). - `CanRestart` is consulted before `Restart`, and a session-managed daemon keeps its running addon copy instead of having a competing `fcitx5 -r` spawned next to it. Only a refused controller request uses the CLI fallback. - `parse_maps_plugin_path` no longer treats the kernel's " (deleted)" suffix as "fcitx5 is not running", so the stale-copy case is reported and judged correctly.
The host's MSRV is 1.95 while CI lints with the current stable (1.98), and clippy 1.95 flags these two dedupe decisions as `nonminimal_bool`. Rewriting them as `is_none_or` (already the shape used in the history cache) is equivalent — signal/local is None, or the stamp is outside the dedupe window — and is clean on both toolchains. Package revision advances to 105 for the rebuilt deb/rpm.
The bridge refused any snapshot over 16 MiB by dropping the window connection. The window treats a dead host as "the host is gone" and exits successfully, and the host only records a window error on a non-zero exit — so an oversized view model turned into: connection dropped, window exits, host respawns after 800 ms, same oversized snapshot, forever. The window blinked about once a second and the app was effectively unusable (it takes ~14k entries at the current average transcript length to get there, but history has no entry cap by default). Now the host trims the oldest history before sending: a ladder of 4000/1000/200/0 entries, newest first, until the payload fits a 12 MiB soft limit. The history list is refilled from the real cache every tick, so trimming only affects the frame being sent, and both sides keep indexing the same list. The count of hidden entries travels in `FrontendViewModel::history_truncated` and the history page paints a warning with that count plus the setting that actually shrinks history. If even the empty history does not fit, the frame is skipped and the connection is kept: the window keeps showing the last good state instead of dying in a loop. `OPENLESS_UI_SNAPSHOT_LIMIT=<bytes>` lowers the soft limit so the degraded path can be exercised on a device without accumulating 12 MiB of history. Verified: with the limit forced to 60 kB the host logged the trim once, the window process was spawned exactly once and never exited, and steady-state publishing stayed at zero frames (the fingerprint gate still works). Package revision 106.
wgpu 28 把 `MemoryHints::Performance` 定为默认值,于是 wgpu-hal 的 Vulkan 子分配器
按 device 128–256 MB 的内存块向驱动申请。每个渲染进程(主窗、胶囊、每个弹窗)都为
此付出固定代价:建 device 后显存 195 MiB,且与窗口大小、UI 内容无关 —— 460×180 的
胶囊要 197 MiB,而裸 Vulkan(vkcube,含交换链)只有 5 MiB。
现在 eframe 窗口与原生 layer-shell 胶囊共用一份设备描述,统一改要
`MemoryHints::MemoryUsage`(8–64 MB 块)。
本机实测(GTX 1660 SUPER / NVIDIA 615.71.09 / 1920×1080@120):
胶囊 460×180,Siri 录音态: 198 MiB → 18 MiB,29.8 fps 不变
设置面板打开(背板 4×MSAA + 模糊 + 动画预览):
209 MiB → 47 MiB,72.7 fps 不变
顺带加入 `frame_stats`:`OPENLESS_FPS_LOG=1` 每秒写一行帧统计,
`OPENLESS_FPS_SETTINGS=1` 启动即打开设置面板,用来量上面这两组数字。
WGPU 迁移把包依赖从 libegl1 / libwayland-egl1(rpm: libglvnd-egl / libwayland-egl.so.1)换成了 libvulkan1(rpm: vulkan-loader)+ Recommends mesa-vulkan-drivers,但 linux-egui-release-contract.test.mjs 还在断言旧的 EGL 依赖,于是 CI 的 macOS/Windows 契约步骤从迁移那次提交起就红: AssertionError: deb must require libegl1 at scripts/linux-egui-release-contract.test.mjs:98 现在断言 Vulkan loader 在 deb/rpm 依赖里、EGL 依赖不再回来(回来了说明 GL 渲染 路径又被引回来),并断言 Recommends 里有 Vulkan 驱动。 顺带更新 docs/linux-egui-dependency-upgrade.md:layer-shell 胶囊已经走 wgpu/Vulkan,「仍存在的 GL 路径」一节以及验收清单里「该 surface 仍走 GL」的说法 都过期了;补上 MemoryHints::MemoryUsage 的显存结论。
`cargo clippy -p openless-linux-egui --all-targets -- -D warnings` 会把 `assert!(MAX_TEXTURE_DIMENSION_2D >= 3840)` 判成 assertions_on_constants。 挪进 const 块,仍然是编译期拦截「把 2D 纹理上限调小」的改动。
历史保留用的是两套时间:`created_at` 由注入的 `Clock` 生成,而保留截止 (`retain_with_policy`)和「最近 N 分钟」(`recent_within_minutes`)自己读 `chrono::Utc::now()`。注入固定时钟的后端(测试、回放)因此在真实时间越过 「固定时间 + retention」之后,会把刚写入的条目在写入瞬间判成过期删掉: api::tests::completed_dictation_persists_history_and_activity_from_the_session_snapshot assertion `left == right` failed left: 0 right: 1 (api.rs:11048) 本 PR 12:13Z 那次 CI 还是绿的,12:55Z 起每次都红在同一行 —— 这个测试注入 2026-08-28T12:34:56Z、retention 30 天,真实时间在 2026-09-27T12:34:56Z 越线。 改法:`HistoryStore` 持有唯一时钟来源,后端组装时用 `attach_clock` 把注入的 `Clock` 交下去;保留裁剪与 `recent_within_minutes` 都改用它。功能、默认值 (7 天)、`0 = 不按天数清理`、只在写入时裁剪的行为一律不变;生产注入的本来 就是 `SystemClock`,行为零变化。 回归测试 `retention_follows_the_injected_clock_instead_of_the_wall_clock` (固定时钟 + 30 天保留:新条目留住、超期条目照删;把 cutoff 换回 `Utc::now()` 时该测试立刻失败,已验证)。 package revision 118。
现场:一个跑了一晚的 UI 进程 RSS 5.8 GB(VSS 7.2 GB),但 21 小时只用了
60 秒 CPU、GPU 显存 47 MiB —— 涨的是纯内存,不是渲染,也不是泄漏式的匀速增长。
成因是三段代码凑在一起:
1. 宿主 `publish_view_model()`:内容没变也**每 2 秒发一份保活快照**。
2. UI 侧收件通道是无界 `mpsc::channel()`(`UiBridgeClient::connect`)。
3. 窗口被遮挡/不可见时,eframe 会**完全跳过 egui pass**
(eframe 0.36 `wgpu_integration.rs` 的 `if !show_ui { /* 不做任何 egui pass */ }`),
`UiClientApp::ui()` 根本不会被调用,也就没人排空那个通道。
于是「窗口不可见」的每一秒都在堆快照:21 小时 ≈ 3.7 万份 × ~154 KB ≈ 5.8 GB,
且窗口恢复可见后要先逐份消化积压再回到正常帧,看起来就是卡死。
旁证:健康实例每 40 秒左右会写一条 `[ui-client] ipc round-trip ...`(UI 每 2 秒
一次 ping),而那个实例 21 小时里一条都没有 —— 它的 egui pass 一直没跑。
改法:快照不再走通道,只进 `UiBridgeClient` 的**单一槽位**(`LatestSnapshot`)。
旧快照本来就会被后一份覆盖,中间那些没有任何价值;窗口每帧 `take_snapshot()`
一次即可。内存因此与「窗口被遮挡多久 / 宿主发过多少份」无关。
控制帧(Ready/Hotkeys/Pong/Focus…)照旧走通道:它们由事件驱动、体积小。
回归测试 `an_undrained_window_keeps_only_the_newest_snapshot`:连发 40 份快照、
一份都不取,断言只有序号 40 留在槽位、取走后清空、通道里一句快照都没有
(把快照改回走通道时该测试立刻超时失败,已验证)。
另外这次同步把 origin/beta 的两个提交(Open-Less#1113 空格/鼠标热键、Open-Less#1115 GPT-6 温度)
合入本分支,无冲突。
package revision 120。
以 Tauri(`src/pages/settings/`)为唯一标准,把「AI 服务与模型 → 语言模型」
这一页补到对齐。用户在 Linux/Windows 两边截图对比后指出「差的有点多」。
新增(Tauri 有、我们以前没有):
1. **识别管线**(`ProvidersSection` 的 pipelineMode 行):`识别管线 ⓘ` +
`[传统模式][多模态模式]` 分段控件 + 「两种模式使用完全独立的凭据配置…」
说明。只在实验性开关 `multimodalPipelineEnabled` 打开后出现 —— 与 Tauri 的
`if (prefs?.multimodalPipelineEnabled !== true) return null` 同条件。
切换只改偏好(`pipeline_mode`),两套凭据都留在凭据库里。
2. **渠道卡片**(`ChannelList` 的行)改成 Tauri 的结构:
- 圆角 12 卡片,仅「当前使用」的渠道有中性灰底 + 0.5px 描边(Tauri 特意
不用蓝底竖条);第二行 `provider · model`(模型等宽),名字为空时标题
回退成供应商名(`channel.name.trim() || providerLabel`)。
- 状态块:`上次验证` + 通过(+耗时)/ 失败(+错误 + 「验证失败不会自动停用…」)
/ 尚未验证,再跟「多久以前」,超过 24 小时追加「结果已超过 24 小时」。
- 右侧只留「重新验证 / 启用 / 编辑 ›」(编辑按钮带铅笔与右尖括号图标)。
- 换顺序靠左侧拖拽手柄(新图标 `Grip`,走 lucide 同步管道),不再用 ↑↓。
- 点「验证」后按钮立刻变「正在验证…」并禁用,结果回包或超时后恢复
(Tauri 的 `testingIds` 也是界面本地状态)。
- 供应商选择与「删除渠道」(两步确认)移进编辑面板 —— Tauri 把它们放在
ChannelModal 里,卡片上不出现;卡片下方保留「凭据保存在系统凭据库中。」
数据管道:`SettingsChannel` 补 `id`,并把原来宿主预格式化的 `last_check` 字符串
换成结构化字段(`last_ok` / `last_error` / `last_latency_ms` /
`last_check_age_seconds`),本地化仍全部走 i18n 目录。
文案:新增 15 个 key(识别管线 5 个、渠道状态 10 个),值取自 Tauri 五语目录,
`sync-egui-i18n.mjs` 报告全部 in sync。
测试:`the_pipeline_mode_row_only_shows_with_the_experimental_switch`、
`a_failed_channel_card_explains_the_failure_and_the_stale_result`、
`a_verify_click_marks_the_channel_as_testing_until_the_result_arrives`;
识别管线那条做过负向验证(去掉开关判断即失败)。
package revision 121。
… links
三处对齐 Tauri(用户逐项指出):
1. **渠道编辑器改成右栏覆盖**(`ChannelModal` 的 embedded 形态)。
之前是卡片下方展开的内嵌面板,后来我先做成了全窗口遮罩 + 居中卡片,用户
指出「应该是在右侧覆盖,不是独立的覆盖层」——Tauri 桌面端确实是
`createPortal` 进右侧 `.ol-settings-content-pane`,用
`.ol-channel-dialog-embedded { position: absolute; inset: 0; background:
var(--ol-sidebar-bg) }` 整栏盖住。现在照此实现:
- 头部:返回按钮(`chevLeft`,新图标走 lucide 同步管道)+ 「编辑渠道」+
渠道种类徽章(文字处理 / 语音识别)+ 自动保存说明;
- 中部滚动区:白底圆角表单卡(`服务连接` → 供应商 → 渠道名称(可选)+ 说明
→ 凭据 → 模型),字段照 Tauri 是整列宽、38px 高(`layout::text_input_sized`);
- 底部:左侧「删除渠道」(垃圾桶图标 + 两步确认),右侧自动保存提示 +
保存字段/清除 Secret + 「完成」(蓝色主按钮)。
`SettingsProviderEditor` 增 `is_asr`(宿主 `ProviderEditorForm.kind`)决定徽章。
2. **设置弹窗左栏几何**:底色以前画在「内容高度」的 Frame 上,内容短时下半截
露出内容区底色;内容宽度又比 Tauri 少了 24px(选项 170 vs 190)。改为按整栏
高度铺底色 + 右侧 0.5px 分隔线(Tauri 的 `borderRight`),内容走 12/16 内边距,
选项与「帮助中心」上方的分隔线因此与 Tauri 同宽同位置;不再用 `ui.separator()`
以免它自己的宽度把右栏挤窄。
3. **外链指错了组织**:帮助中心/发布日志/GitHub/反馈都指向
`github.com/earendil-works/openless`,Tauri 用的是 `Open-Less/openless`
(帮助中心还要带 `#readme`)。抽成常量并加回归测试。
测试:`the_channel_editor_covers_the_content_column_with_tauri_chrome`(断言编辑器
画出编辑渠道/服务连接/渠道名称/删除渠道/完成,且设置页正文不透过)、
`settings_links_match_the_tauri_targets`;i18n 新增 6 个键(值取自 Tauri 五语目录)。
package revision 122。
…gh it 按 Tauri 补齐两处行为(用户要求「都要同步」): 1. **字段修改后自动保存**(Tauri `CredentialInput` 的 300ms 防抖 + 失焦/离开前 flush)。UI 侧记一个「防抖截止时刻」在 egui 临时数据里:任一字段(名称、 Endpoint、模型、Resource ID、Auth 模式、密钥、供应商切换、模型预设)改动即标脏, 到点推一次 `SettingsProviderSave`;`完成`/返回先 flush 再关。 因此底部去掉「保存字段/Secret」按钮,只剩「清除 Secret」与「完成」。 ——保留「清除 Secret」是有意的差异:Tauri 的密钥字段会读回已存值(清空输入框 即删除),而我们的密钥草稿始终是只写的(已存密钥不进 UI 进程),没有这个按钮就 无法擦除已保存的密钥。若要完全对齐,需要把密钥读回界面,安全性上是降级的。 2. **「添加渠道」走同一个编辑器**(Tauri `startCreate`):点按钮先在后台建一张空 渠道(名字留空、供应商取预设第一项),宿主 `ChannelCreated` 后自动打开编辑器并 标记为草稿 → 标题是「添加渠道」;用户完全没交互就关闭时把这张空渠道回收掉 (Tauri `shouldRecycleDraft` + `deleteChannelIfBlank`)。原来的内嵌小表单 (`add_channel_form` + `SettingsChannelFormOpen/Provider/Name/Create` 四个 action 与三个 VM 字段)整块删除。 测试:`editor_auto_save_debounces_and_flushes_before_closing`(防抖窗口内不保存、 到点只保存一次、关闭前 flush、干净关闭不保存)、右栏覆盖测试新增草稿标题断言、 `typed_text_survives...` 里那段内嵌表单断言改成编辑器字段断言。 package revision 123。
用户报「打开以后只能显示模型编辑页面,不能改其他设置了」——上一条把渠道编辑器改成 右栏整栏覆盖时漏了两道闸,两处都补上: 1. **别自动弹编辑器**。面板加载后宿主会为「选中渠道」把编辑器拉起来(`selected_channel_id` 的兜底是 active provider 或第一个渠道),这在编辑器以内嵌小面板呈现时无害,改成 整栏覆盖后就变成「一进设置右栏永远是模型编辑器」。新增 `editor_requested`:只有用户 点了「编辑」(`SettingsChannelSelect`)或刚建出草稿(`ChannelCreated`)才允许打开, 面板刷新(保存 / 验证 / 切页 / 保活)不再顺手打开。 2. **覆盖范围跟着分区走**。`channel_editor_panel` 现在只在「AI 服务与模型」这一页替代 `panel()`;切到别的分区时宿主还会主动收起编辑器(Tauri 的 modal 宿主挂在服务页里, 切页即卸载),未交互的草稿照旧回收。 顺带修好 QA 开关 `OPENLESS_FPS_SETTINGS=1`:它以前只在窗口初始化时置一次 `settings_open`,随后被宿主快照覆盖,等于失效;改成每帧采纳快照之后再压一次。 测试:`the_channel_editor_never_covers_other_settings_sections`(通用/外观/关于三个 分区开着编辑器也不许出现「编辑渠道」)。 对照用户数据确认:这次误判没有造成删除——`credential-metadata.json` 仍列出 `llm: deepseek`,mtime 未变,日志无删除记录。 package revision 124。
…esh backend-tests lock
上一轮 `cargo update` 把 CI 全部打挂,三个独立原因:
1. **MSRV 被突破**(macOS Rust 1.88 那个 job)
`aes 0.9.3` 与 `uuid 1.28.0` 都要求 rustc 1.89,而 Core 与 Tauri 声明 1.88:
```
error: rustc 1.88.0 is not supported by the following packages:
aes@0.9.3 requires rustc 1.89
uuid@1.28.0 requires rustc 1.89.0
```
三个锁文件(根 / `src-tauri` / `src-tauri/backend-tests`)里都钉回
`aes 0.9.2`、`uuid 1.27.0`。
2. **sherpa-onnx 包装 crate 与 -sys 版本错配**(Windows / macOS / Android 三个 job)
`cargo update` 把 `sherpa-onnx-sys` 推到 1.13.8,而 `sherpa-onnx` 仍被 `=1.13.2`
钉着,于是 sys 新增的三个字段在旧包装里没有初始化:
```
error[E0063]: missing field `window_shift_ratio` in initializer of
`sherpa_onnx_sys::OfflineSpeakerSegmentationPyannoteModelConfig`
```
直接把 `sherpa-onnx` 升到 1.13.8(Windows static / Android shared 两处),
与 sys 对齐;Android 预编译库清单同步更新:
`scripts/sherpa-onnx-android.mjs` 的版本、归档大小 46_093_321、
SHA-256 `2ff63469…4f44`(已下载校验并解包确认 ABI 目录结构不变)。
3. **第三个锁文件没刷新**(Linux job)
`src-tauri/backend-tests` 是**独立 workspace**,有自己的 `Cargo.lock`;CI 用
`--locked`,于是直接失败:
```
error: cannot update the lock file .../backend-tests/Cargo.lock because --locked was passed
```
`cargo update` 刷新该锁并同样钉住 aes/uuid。
同时把这三个锁都写进 `docs/dependency-audit.md`——之前只记了两处,漏了
`backend-tests`。
验证:core 与 linux-egui 全量测试通过、clippy `-D warnings` 干净、
`sherpa-onnx-android.mjs` 用新校验和成功下载并解包。
上一版把 `uuid` 钉到 1.27.0 仍然不行:1.27 和 1.28 都要求 rustc 1.89, 只有 1.26.1 及更早声明 1.85。 ``` error: rustc 1.88.0 is not supported by the following packages: uuid@1.27.0 requires rustc 1.89.0 ``` `src-tauri` 与 `src-tauri/backend-tests` 两个锁文件都钉到 `uuid 1.26.1` (MSRV job 只检查这两个 workspace,根 workspace 的 Linux 宿主声明的是 1.95)。 本地用 `rustup toolchain install 1.88.0` + 与 CI 完全相同的命令复验: `cargo +1.88.0 check --locked --manifest-path src-tauri/Cargo.toml` 不再报 MSRV 冲突,编译推进到本仓自己的 `compile_error!`(Linux 不是 Tauri 目标) 才停,说明整个依赖图对 1.88 干净。
…ld deps 上一版为了守住 Rust 1.88 把 `aes`/`uuid` 往回钉,等于用降级依赖换工具链。 反过来做:1.88 已经装不下当前依赖树,直接把 MSRV 提到 1.89,依赖保持最新。 - `openless-core` / `src-tauri` / `src-tauri/backend-tests` 的 `rust-version` 1.88 → 1.89(`linux-egui` 本来就是 1.95,不动) - 三个锁文件放开:`aes 0.9.3`、`uuid 1.28.0`(1.28 是当前最新,1.27/1.28 都要求 1.89;1.26.1 及更早是 1.85) - CI:`macOS Rust 1.88 MSRV` → `1.89 MSRV`,工具链与 `cargo +1.89.0` 两处命令同步,job 名与文档里的表格一起改 - README(中/英)与 `docs/ci-trigger-and-cache-policy.md` 的最低工具链承诺 同步到 1.89,并写明这个数字由依赖实际要求决定(aes 0.9.3 / uuid 1.28) 本地用与 CI 相同的命令验证: `cargo +1.89.0 check --locked --manifest-path src-tauri/Cargo.toml` 与 `cargo +1.89.0 test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --no-run` 都不再报 MSRV 冲突(编译推进到本仓自己的 `compile_error!`,即 Tauri 不支持 Linux 的那条)—— 说明整张依赖图对 1.89 干净。 根 workspace:core + linux-egui 编译、全量测试、clippy `-D warnings` 全部通过。
按「全部升到最新」执行,Rust 与 Tauri 是同一件事——tauri 2.12 的最低要求 就是 rustc 1.90,索性把 MSRV 直接对齐当前 stable。 ## Rust 1.99 - `openless-core` / `src-tauri` / `src-tauri/backend-tests` 1.89 → **1.99** - `linux-egui` 1.95 → **1.99**(全仓一个数字,不再有漂移;egui 0.37 本身只要 1.95) - CI:MSRV job 工具链与两条 `cargo +1.99.0` 命令同步,job 名 "Rust 1.99 MSRV" - README(中/英)与 `docs/ci-trigger-and-cache-policy.md` 的公开承诺同步为 1.99, 并写明该数字的由来(tauri 2.12 → 1.90,aes 0.9.3 / uuid 1.28 → 1.89, 这里直接跟随 stable) ## Tauri 2.12.4 整条训练线 + 插件一起升(插件的新版本要求 `tauri-utils ^2.10`,只存在于 2.12, 所以必须成套动): | crate | 旧 | 新 | |---|---|---| | tauri | 2.11.6 | **2.12.4** | | tauri-build | 2.6.3 | **2.7.3** | | tauri-runtime | 2.11.3 | **2.12.2** | | tauri-runtime-wry | 2.11.4 | **2.12.2**(连带 wry 0.55.1 → 0.57.0) | | tauri-macros | 2.6.3 | **2.7.3** | | tauri-utils | 2.9.3 | **2.10.2** | | plugin-shell | 2.3.5 | **2.5.0** | | plugin-dialog | 2.7.2 | **2.8.3** | | plugin-fs | 2.5.1 | **2.7.1** | | plugin-updater | 2.10.1 | **2.14.0** | | plugin-single-instance | 2 | **2.5.4** | | plugin-autostart | 2 | **2.7.0** | JS 侧此前 `npm update` 已是 2.12 系(`@tauri-apps/api 2.12.4`、plugin-dialog 2.8.3、 plugin-shell 2.5.0、plugin-updater 2.14.0),这次 Rust 侧追平,消除 JS/Rust 版本错配。 对照 2.12 changelog 核对过两处会打到我们的改动: - `tauri-runtime` 的 `primary_monitor`/`available_monitors`/`monitor_from_point` 改为返回 `Result` 并在主线程查询 —— `capsule_mirrors.rs` 与 `lib.rs` 本来就写成 `.ok()` / `.ok()?` / `.unwrap_or_default()`,无需改。 - 新增 `tauri::Error::UnexpectedMenuKind` —— 只对我们穷尽 `match tauri::Error` 才破坏, 我们只构造 `Error::Anyhow`,不匹配它。 注意:`tauri 2.12` 的 Android 模板把 Gradle 脚本迁到 `compilerOptions`,最低 Gradle 8.13;`gen/android` 由 CI 的 `tauri android init` 重新生成,无需改仓库文件。 本地验证:core + linux-egui 全量测试、clippy `-D warnings` 通过; `cargo +1.99.0 check --locked --manifest-path src-tauri/Cargo.toml` 与 backend-tests 的 MSRV 门禁干净(编译推进到本仓自己的「Tauri 不支持 Linux」 `compile_error!` 为止 —— 该 workspace 无法在 Linux 上完整编译,三端由 CI 验证)。 deb/rpm 重新打包并自检:`OpenLess 2.0.2-Beta.1-139`。
…OS gate
## 缓存
`Cache maintenance` 从 10/05 起每周失败,beta scope 已经涨到 10.5 GB(预算
10 GB),平台只能按 LRU 静默淘汰 —— 这就是"有缓存却总在冷编译"的根因。
失败原因不是脚本逻辑,而是 GitHub 改了接口:
gh: Invalid request. Missing required query parameter key (HTTP 422)
- `scripts/ci-cache-usage.sh`
- 删除改为同时带 `key` 与 `ref`(`ref` 里含 `/`,统一百分号编码);
- 新增 `--keep-family N`:同一个(分支, key 家族)只保留最新 N 代,
其余作为"被取代的旧兄弟"删除。key 家族 = 去掉尾部内容哈希后的前缀,
例如 `v0-rust-cross-platform-Windows_NT-x64-3619e479-a77d3f83`
→ `v0-rust-cross-platform-Windows_NT-x64`;因为 `restore-keys` 只需要
上一代做回退,默认保留 2 代;
- 新增 `--dry-run`:只打印计划,不删除;
- 永远不碰 tag scope(tag 运行只能读默认分支缓存,发布缓存按 tag 写入)。
- `scripts/ci-cache-usage.test.mjs`:假的 `gh` 现在记录完整 URL,断言删除请求
同时带 `key=` 与编码后的 `ref=`;新增同族剪枝、`--dry-run`、非法
`--keep-family` 的用例。
- `.github/workflows/cache-maintenance.yml`:改成
`--prune --keep-family 2`,让每周任务真正回收。
- `.github/workflows/check-linux-egui.yml`:把这个契约测试接进 CI —— 之前它
没有任何 workflow 在跑,接口变更因此没被任何门禁发现。
## 停止每次 push 写发版缓存
`release-tauri.yml`、`android-apk.yml` 原本都监听 `push: branches: ['**']`,
每次 push 跑 18-25 分钟并往 beta scope 里写一份 release/APK 依赖树
(macOS release 710+706 MB、Windows release 886 MB、Android APK 553 MB +
Gradle ~1 GB)。改成:
- push 只tag、每周一次 `schedule`(beta 上重建 + 预热)、`workflow_dispatch`;
- tag 运行照旧发布,schedule 运行 `OPENLESS_RELEASE_IS_TAG=false` 不会发布。
## 鸿蒙门禁
`harmony-check` job(ubuntu-latest,跟着 `tauri` 改动区域门控):
- `scripts/audit-harmony-ipc.mjs` + `git diff --exit-code harmony/ipc-audit.json`
—— 报告是提交进仓库的,漂移过就说明 ArkTS 覆盖被悄悄改变;
- 8 个 ArkTS/IPC 契约测试显式跑一遍(此前只藏在 `npm run build` 的 prebuild 里);
- `VITE_OPENLESS_PLATFORM=harmony npm run build -- --base=./ --outDir=dist-harmony`
构建 ArkWeb 侧产物。
完整的 HAP 打包需要华为 DevEco / ohpm SDK,hosted runner 上没有;Rust 侧仍由
Linux job 的 `cargo test -p openless-harmony-host` 覆盖。
顺带把 `harmony/ipc-audit.json` 刷新到当前代码(190 → 191 条命令,1 条从
rust 变为 arkts,新增未实现的 `open_android_local_migration`)。
验证:`ci-cache-usage.test.mjs`、`ci-changed-areas.test.mjs`、8 个鸿蒙契约、
harmony 前端 bundle 构建全部通过;实盘 `--prune --keep-family 2` 把
36 → 30 个缓存、10.70 → 10.21 GB。
… path, share caches ## Linux 随 Release 一起发布(不再人工附加) - `release-linux-egui.yml` 增加 `tag`(必填)+ `publish` 输入,并把 `checkout_ref` 传到可复用工作流 —— 构建的 commit 就是那个 tag,`package.json` 版本对不上直接失败; - `check-linux-egui.yml` 新增 `release_tag` / `publish` / `checkout_ref` 输入, 校验和额外产出 `SHA256SUMS-linux`(不与桌面/安卓的同名文件冲突); - 新增 "Attach Linux packages to the GitHub Release" 步骤:等待桌面流水线把 Release 创建出来(最多 10 分钟),然后 `gh release upload --clobber` 附加 deb、rpm、`SHA256SUMS-linux` 和 `OpenLess_linux_x86_64.*` 直链别名; - 权限从 `contents: read` 改为 `contents: write`(只有 release 入口用得到)。 ## HarmonyOS 发布链路(新) - `.github/workflows/release-harmony.yml`:手动触发、必须带 tag、默认在 `[self-hosted, harmony]` 上跑(DevEco/HarmonyOS SDK 与发布签名 profile 都是 账号受限的,托管 runner 无法构建 HAP)。步骤:校验 tag/版本 → 从 Secrets 还原签名材料 → 构建+签名+打包 → 校验 SHA256 → 上传 artifact → 附加到同一个 GitHub Release → 上传华为应用市场; - `scripts/package-harmony-hap.sh`:复用现有 `build-harmony-frontend.sh`, 用 release 材料重新签名,产出 `OpenLess_harmony_arm64.hap` + 直链别名 `OpenLess_harmony.hap` + `SHA256SUMS`。 构建前把 `AppScope/app.json5` 的 `versionName`(取自 package.json)和 `versionCode`(取自 `linux-egui/package-revision`,单调递增)改写进去, 结束后恢复(含失败路径的 trap),因为仓库里是 0.1.0/1 的开发占位值; - `scripts/sign-harmony-hap.py`:支持 `--release`(`release.cer/p7b/p12`、 可配置 alias),密码可来自环境变量,输出统一为 `entry-default-signed.hap`,日志里对密码做 `[redacted]`; - `scripts/upload-appgallery.mjs`:AppGallery Connect 发布 API (token → `upload-url/for-obs` → PUT 对象 → `app-package-info`), 支持 `--dry-run` 与 `--skip-if-unconfigured`(没有 AGC 凭据时跳过商店上传、 仍然发布 GitHub Release)。只替换候选包,不代替人工提交审核。 ## release 复用 beta 缓存 `swatinem/rust-cache` 的 key 只有在没有 `shared-key` 时才带 job 名,所以此前 release 与基线永远不共享缓存。现在: - `ci.yml`:Windows → `shared-key: tauri-windows`,macOS → `shared-key: tauri-macos` (替换原来私有的 `key: macos-stable-v1`); - `release-tauri.yml`:两处缓存改用同一 `shared-key`(删掉 `macos-release-v1`), tag 构建直接恢复 beta 维护的那棵树;每周一 04:17 的 schedule 运行会在 beta 上 把 release profile 产物写进同一个缓存,因此后续 tag 是热的; - Linux 本来就共用同一个可复用工作流(beta push 会顺带构建 release 二进制)。 ## 门禁 - `scripts/ci-cache-sharing.test.mjs`:断言 ci.yml 与 release-tauri.yml 的 `shared-key` 一致、release 不再有私有 key、基线写入仍然只限 beta、 Linux 入口复用同一可复用工作流且 pin 了 tag; - `scripts/harmony-release-contract.test.mjs`:断言 Harmony 发布会话 dispatch-only、pin tag、默认 self-hosted、凭据全部走 Secrets/Variables、 打包/签名/商店上传三个脚本的契约; - 两个新测试都接进 Linux 契约步骤;`linux-egui-release-contract.test.mjs` 按新策略更新(Linux 现在自动附加、`contents: write`、`SHA256SUMS-linux`)。 文档:`RELEASING.md` 更新 Linux 段落,并新增 HarmonyOS 签名材料/AppGallery 凭据清单(全部为 Secret/Variable,无字面量)。
…K tarball 调研了社区怎么做(openharmony-rs/setup-ohos-sdk、harmonyos-dev 的 Action 示例、 phodal 的实践、华为自己的 CodeCI 文档),并用真机验证了一遍: 结论 —— **公开的 OpenHarmony SDK 装得上,但 hvigor 不认**。 - 本地实测:`repo.huaweicloud.com` 的 OpenHarmony 6.0 SDK(2.4 GB)里 `@kit.IMEKit.d.ts`、`@kit.AssetStoreKit.d.ts`、`hap-sign-tool.jar`、 `aarch64-unknown-linux-ohos-clang` **全都有**,也就是说华为账号不是必需的; - `@ohos/hvigor@6.26.8` + `@ohos/hvigor-ohos-plugin@6.26.8` 可从 `https://repo.harmonyos.com/npm/` 公网安装; - 但 hvigor 的 `HmosSdkLoader` 只认 DevEco 那套布局/版本耦合,按 `default/openharmony` 铺好后仍然报 `Cannot find the corresponding SDK version`(补 `js`/`previewer` 组件、 改 `compatibleSdkVersion` 为 6.0.0(20) 都不解决)。 社区的做法因此是「把 SDK 打包放到自己能读的地方,按校验和固定」,而不是在 CI 里现场下载 —— 这正是本次实现的路径: - `scripts/prepare-harmony-sdk.sh`:从 `HARMONY_SDK_URL` 下载、校验 `HARMONY_SDK_SHA256`、按 DevEco 布局铺到 `$TOOLCHAIN/sdk/default/{openharmony,hms}`,并用华为 npm 源安装 hvigor; SDK 由 `actions/cache` 以校验和为 key 缓存,一次下载多次复用; - `release-harmony.yml`:默认 `runner_labels: ["ubuntu-latest"]`,优先用 `HARMONY_SDK_URL`/`HARMONY_SDK_SHA256` 两个仓库变量准备 SDK;没有这两个变量时 才要求自托管 runner(提示写明怎么补),不再默认排队等一个不存在的 runner; - `package-harmony-hap.sh` / `build-harmony-frontend.sh`:没有 DevEco 的机器用 `node @ohos/hvigor/bin/hvigor.js` 驱动构建(`HVIGOR_JS`),有 DevEco 的机器 继续用 `hvigorw`; - `RELEASING.md`:写清两条路径、变量清单,以及「SDK 不能提交进仓库(许可证不允许 再分发)」,并记录上面那条实测结论,避免下一个人重复踩坑。 契约测试 `harmony-release-contract.test.mjs` 同步更新:默认托管 runner、 SDK 走校验和、缺 SDK 时的报错文案、以及独立 hvigor CLI 的契约。
…signed channels 鸿蒙端补齐:从「能签个名」变成「CI 里能真的产出、能校验、按渠道分发」。 构建(本机实测跑通,工具链 = HarmonyOS Command Line Tools 26.0.0.851、SDK API 26): - `scripts/prepare-harmony-sdk.sh`:从 `HARMONY_SDK_URL` 下载 + 校验 `HARMONY_SDK_SHA256`,按 DevEco 布局铺开,支持「裸 SDK」和「整套命令行工具」 两种包;先验证 `sdk-pkg.json`(含 apiVersion、ets/toolchains 组件)再继续, 把 hvigor 那句含糊的 `Cannot find the corresponding SDK version` 变成可执行提示; 有 `bin/hvigorw` 就用工具链自带的,没有才从华为 npm 源装独立 hvigor (**绝不**装进工具链的 `hvigor/`,那会引入第二个 @ohos/hvigor 单例, 导致 `The root node is not yet available for build` —— 已踩过并修掉); - `scripts/pack-harmony-toolchain.sh`:把本机工具链打成 CI 用的 tar.zst (显式成员列表,只排除 emulator;按名字排除 previewer 会连带删掉 hvigor 插件自己的 `tasks/hook/previewer`,构建直接失败),实测 1.63 GiB, 低于 Release 附件 2 GiB 上限; - `scripts/build-harmony-backend.sh`:工具链路径变化时清掉 aws-lc-sys 的 CMake 产物。它把检测到的架构缓存进构建目录,换工具链后会拿 aarch64 编译器 去汇编 x86_64 源码; - `scripts/package-harmony-hap.sh`:`OPENLESS_HARMONY_CHANNEL=release|test|none`, 同时产出未签名包和渠道签名包; - `scripts/verify-harmony-hap.sh`(新):不接设备就能校验 HAP——ArkWeb 产物、 hashed bundle、`.so`、versionName/versionCode,以及 `hap-sign-tool verify-app`。 分发策略(用户要求):仓库/Release 只放未签名包,AppGallery 正式版用 `release.*` 材料,测试渠道用 `test.*` 材料,同一份签名脚本用 `--prefix` 切换; `release-harmony.yml` 新增 `channel` 输入,Release 上传步骤只放未签名件, AppGallery 上传步骤只放签名件,`upload-appgallery.mjs` 接受 `--channel`。 CI:`ci-changed-areas.sh` 新增 `harmony` 区域(只有散文与 Linux 专属目录可跳过), `ci.yml` 发布该输出并以 fail-open 方式门控 `harmony-check`。 本机产物(未签名,已放 /home/aeoform):OpenLess_harmony-2.0.2-Beta.1-140-unsigned.hap; Linux 侧同 revision 的 deb/rpm 也已重新构建并校验通过。
GitHub rejected the whole workflow file, so every run for the commit ended with `startup_failure` and no jobs. `runner` is only available inside a step; resolve $RUNNER_TEMP there and export the signing directory through $GITHUB_ENV. Verified with actionlint, which now passes for every workflow in the tree.
check-linux-egui.yml asked for contents: write while its callers grant contents: read, which GitHub rejects before the run starts - the symptom is `startup_failure` with zero jobs. Drop the workflow's own permissions so it inherits the caller's grant, and let release-linux-egui.yml - the only caller that uploads release assets - grant write.
A stale SHA256SUMS from an earlier revision made verify-linux-egui-packages.sh fail on freshly built packages, and the workflow regenerated it by hand. Write it where the packages are produced.
仓库本身只有 1400 个跟踪文件 / 44 MB,没有编译产物或临时文件入库;真正的垃圾是 一批「某次操作的过程记录」和指向已删除文件的说明。逐项核对后处理如下。 删除(26 个文件,全部是某一次会话/某台机器的过程记录,结论已在代码或 RELEASING.md 里): - docs/reviews/2026-10-15、2026-10-16:只读审查快照,含他人机器的绝对路径; - docs/local-builds/:2026-09 的本地集成流水账; - docs/research/issue-1192.md:对应 issue 已关闭; - docs/releases/ 下 5 篇带日期的运维笔记(2.0.0 准备、Windows MSI 修复、 2.0.1 CI 隔离、Apple omni CPU TODO、beta7/2.0.1 记录); - docs/linux-egui-tauri2-parity.md(写于 egui 0.36.2、PR Open-Less#1060 时期,已被 linux-egui-handoff 与 known-gaps 取代); - docs/linux-egui-dependency-upgrade.md(内容并入 dependency-audit.md); - docs/ui-ux-refinement.md、docs/local-shortcuts-dropdown-20261016.md、 docs/beta5-regression-repair.md、docs/desktop-beta5-integration.md、 docs/local-asr-verification.md:已发布的 beta.5 / Beta.4 验收记录; - docs/qwen-asr-submodule-upgrade-checklist.md:标题即「已下架」; - docs/references/tauri-webdriver-v2.workflow.yml:一份从没被引用过的外部 workflow 副本; - openless-all/app/scripts/linux-egui-manual-install.sh:给「发布 zip 的散装 安装树」用的脚本,而打包链只产出 deb/rpm,从来没有这个 zip。 修正(都是删完文档后暴露出来的旧引用): - docs/index.md:去掉被删条目,补上 dependency-audit、known-gaps、gemini、 tencent、mobile-ime、remote-input 两篇、homebrew 与两篇发布说明的入口, 删掉「本地集成记录」整节; - docs/linux-egui-handoff/02-gap-register.md:依赖升级那行改指 dependency-audit; - docs/macos-build-performance.md:删掉 MLX 缓存段——`.github/actions/cache-macos-mlx` 在 16a73b9「retire legacy macOS engines」时就已经删了; - docs/experimental-local-asr.md:验收记录链接改为生命周期文档。 补齐(当前结构已经变了): - docs/structure.md:目录树改成真实的仓库根,补 `harmony/`、`mobile-ime/`、 `scripts/linux-*`,去掉早已不存在的 `design_handoff_openless/`、 `video-materials/`、`AGENTS.md`; - docs/architecture.md:工作区成员补 `harmony/rust`,分层表新增 HarmonyOS 行, 说明 Android/HarmonyOS 共用 `mobile-ime/`; - openless-all/README.md:补 harmony / mobile-ime / fcitx5 插件,去掉 AppImage。 全仓 56 篇 md 的相对链接重新校验:0 悬空。
前端死代码:src/components/chat 下 10 个文件没有任何引用——
AgentBuddy.tsx 与 ui/{empty,marker,message,bubble,spinner,card,input-group,input,textarea}.tsx
是 shadcn 风格脚手架残留(AgentBuddy 只剩一条「不应出现」的断言)。
ui/button.tsx 保留:message-scroller.tsx 通过 './button' 引用它。
tsc 与 vite build 均通过。
契约测试同步到 fffdbfd/f18fc39e 的触发变更(release-tauri / android-apk 改为
tag + 每周预热,不再每次分支 push 写入 GB 级缓存):
- android-apk-workflow-contract:期望 push 只有 tag,并断言每周 schedule;
- windows-msi-release-contract:同上,去掉 push.branches 断言;
- workflow-concurrency-contract:改为断言两个 release 工作流不再有 push.branches。\n
两份根目录残留: - `Examples/` 只有一个 `polish-reference-examples.sample.jsonl`(参考改写的 格式样例),是文档而不是代码。移到 `docs/references/`,README 中英文两处 链接同步更新,根目录少一个孤零零的目录。 - `.cargo/config.toml` 只有一条设置(`x86_64-pc-windows-gnu` 的 `-Wl,--exclude-all-symbols`,压住 cdylib 的符号导出表溢出)。cargo 从当前 目录向上找配置,而所有 workspace(app、app/src-tauri、backend-tests、 harmony/rust、linux-egui)都在 `openless-all/app/` 下,所以放到 `openless-all/app/.cargo/` 行为完全一致,但离构建目录更近、不容易被当成 遗留文件删掉。 校验:`cargo check -p openless-core`、`src-tauri` 的 cargo metadata 均正常, 全仓相对链接 0 悬空。
- `openless-all/dist/` 只有一个 `.gitkeep`,而且 `dist/` 在 .gitignore 里, 文件根本没入库;没有任何脚本往这个目录写东西,是早期构建残留。 - `openless-all/scripts/sync-egui-locales.mjs` 是 22 行压缩写法的原型, 已被 `app/scripts/sync-egui-i18n.mjs`(342 行、带文档与大小写/分隔符归一化) 取代,且从未被引用。真正在维护的是后者。 其余保留:`openless-all/scripts/linux-fcitx5-plugin/`(打包脚本与 CI 都直接引用)、 `vendor-dsh-events.sh`(dsh-events 的来源说明与重新 vendor 入口)。
仓库里有两个 scripts/:根目录的(版本号同步)和 openless-all/scripts(fcitx5 插件、 dsh-events vendor 脚本)、以及 openless-all/app/scripts(构建与测试)。后两个都在 app 之下,合并成一处: - openless-all/scripts/linux-fcitx5-plugin → openless-all/app/scripts/linux-fcitx5-plugin - openless-all/scripts/vendor-dsh-events.sh → openless-all/app/scripts/vendor-dsh-events.sh - 删掉空的 openless-all/scripts/ 同步更新引用:package-linux-egui.sh 的 PLUGIN_ROOT、check-linux-egui.yml 的 working-directory、ci-changed-areas.sh 的 area 规则、local_hotkeys.rs 的注释、 README / structure.md / ci-trigger-and-cache-policy.md;.gitignore 去掉早已不存在的 openless-all/scripts/linux-desktop/kde/build/。 校验:ci-changed-areas 契约通过、actionlint 通过、插件 build/ 随目录一起搬过去。
之前这个 job 36 秒就绿,容易被误读成「鸿蒙已经编译通过」。实际它只做三件事: npm ci、IPC 审计与 8 个 ArkTS 契约测试(都是 node 文本检查)、Vite 打包 ArkWeb bundle;ArkTS 一个字节都没编译过。Rust host 是有编译的,但在 Linux job 里 (`cargo test --locked -p openless-harmony-host`)。 改成: - job 名与注释写清楚三层覆盖分别在哪,不再暗示「已编译」; - 新增两个仅当仓库变量 `HARMONY_SDK_URL` 存在时才执行的步骤:缓存工具链、 `prepare-harmony-sdk.sh` + `package-harmony-hap.sh`(channel=none,未签名) 并用 verify-harmony-hap.sh 校验产物。这样配好 SDK 之后,PR 上就会真的 type-check ArkTS、交叉编译 aarch64-unknown-linux-ohos 的 Rust host、并 assembleHap;没配时保持现在的轻量门禁,不拖慢每个 PR。 actionlint 通过。
审计了所有平台的 job,找到三处冗余,前两处直接改掉: 1. macOS MSRV job 与 cross-platform 的 macOS 矩阵重复:MSRV 现在是 1.99,stable 是 1.99.x,同一个 minor 上再跑一遍 macOS runner(macOS runner 也是最难排队的 一类)没有任何额外覆盖。改为在 changes job 里比较 openless-core 的 `rust-version` 与 runner 上 stable 的 minor 号,只有真正落后时才让 `macos-msrv` 跑;哪天 MSRV 落到 1.99 以下会自动恢复,不需要有人记得改回来。 同时加了两条契约断言(输出存在、门控存在),避免以后被静默删掉。 2. Linux job 里 `cargo test -p openless-linux-egui --all-targets` 之后紧跟一条 `cargo check --all-targets`:test 已经把全部 target 编译过一遍,check 只是 第二次全量编译,零额外覆盖。删掉,契约测试改成反向断言。 没动、但记录在这里的:`npm test` 在 macOS 与 Windows 上各跑一遍全量前端测试 (9 个测试文件真的依赖 `process.platform`,保留有意义);ui-motion / android / harmony / cross-platform / linux 各自会执行一次 vite 构建(产物用途不同,共享 artifact 会让每个 job 多一次下载,收益不明显)。 actionlint 与 8 个相关契约测试全通过。
`windows-ime/OpenLessIme.sln` 是独立 vcxproj,Tauri 构建不会碰它,CI 里也没有任何 步骤编过它——一个编译错误只能等到有人在装了 VS 的 Windows 机器上才发现。 Windows 矩阵新增两步:`microsoft/setup-msbuild@v2` + `msbuild OpenLessIme.sln /p:Configuration=Release /p:Platform=x64`,并加契约断言防止被删。 顺带把输入法/Rust 的覆盖矩阵写清楚(见提交说明): | 层 | 是否编译 | 在哪 | |---|---|---| | mobile-ime 的 C++(android_jni.cpp / rime_engine.cpp) | ✅ | android-check 的 build-mobile-rime.py 会跑 mobile-ime 的 CMake(--stage 编 libopenless_rime.so) | | harmony Rust(openless-harmony-host) | ✅ host target | Linux job 的 cargo test -p openless-harmony-host | | harmony Rust(aarch64-unknown-linux-ohos) |⚠️ 需要 ohos clang | 配了 HARMONY_SDK_URL 后的 HAP 步骤 | | harmony ArkTS / bridge.cpp |⚠️ 需要 hvigor | 同上 | | Windows TSF IME | ✅(本次新增) | Windows 矩阵 | | Linux fcitx5 插件 | ✅ | Linux job 的 cmake + ctest | actionlint 与 windows-ime 契约测试通过。
别人是这么做的:k2-fsa/sherpa-onnx 的 `harmony-os` 工作流在 ubuntu-latest 上 直接下载 `commandline-tools-linux-x64-*.zip`(官方工具的公开镜像),缓存、解压、 hvigorw 构建;harmonyos-dev/hos-sdk 走 `sdkmgr install --accept-license` 的同一条路。 也就是说「必须华为账号 / 必须自托管 runner」是我之前的误判。 于是把默认源换成公开镜像,并把产物校验固定下来: - `prepare-harmony-sdk.sh` 内置 `commandline-tools-linux-x64-6.0.1.251.zip` (2,108,510,902 B,sha256 e971348e…),校验不过直接失败;仍可用 HARMONY_SDK_URL / HARMONY_SDK_SHA256 覆盖到新版本; - 判断「已安装」时同时要求 `sdk/…/sdk-pkg.json` 与 `bin/hvigorw`,缓存恢复 不完整时会重新下载而不是带着半个工具链继续; - `ci.yml` 的 harmony job 不再用 `vars.HARMONY_SDK_URL` 门控:每个 PR 都真的编 ArkTS + 交叉编译 aarch64-unknown-linux-ohos 的 Rust host + assembleHap; - `release-harmony.yml` 同样零配置,只保留签名 Secrets; - `build-harmony-frontend.sh` 改用 `OHPM_HOME` 而不是 `ohpm --cache` (6.0.1 的 ohpm 没有这个参数,实测踩到); - RELEASING.md 记录镜像来源、覆盖方式和「不要入库(许可证不允许再分发)」。 本机实测(全新解压的公开工具链,无任何华为凭据): BUILD SUCCESSFUL → OpenLess_harmony_arm64.hap 64,994,614 B, verify-harmony-hap.sh 通过(versionName 2.0.2 / versionCode 140)。
公开镜像是 `commandline-tools-*.zip`,脚本却只会 `tar -xf`,CI 里直接 `tar: This does not look like a tar archive`。改成按下载文件的真实扩展名分派: zip 用 unzip(缺 unzip 时给出明确报错),其余仍走 tar。 本机用 file:// 指向同一个 zip 完整跑通:prepare → 解压 → 校验 API 21 → package(hvigor BUILD SUCCESSFUL)→ 64,994,614 B 的未签名 HAP。
三处环境缺口,都是 CI 里才暴露的: - `package-harmony-hap.sh` 需要 `hvigorw`/`ohpm`/`node`,但命令行工具把这些放在 自己的 `bin/` 与 `tool/node/bin` 里,CI 没人 source 环境 → 提前把这两个目录 放进 PATH,随后版本号读取、ohpm、hvigorw 都能找到; - harmony job 之前没有 Rust,HAP 里的 `libopenless_harmony_host.so` 需要交叉 编译 → 加 `dtolnay/rust-toolchain@stable`(`targets: aarch64-unknown-linux-ohos`, stable 就有这个 tier-2 target)与独立的 rust-cache key; - 下载实测 108 MB/s(2 GB 用 18 秒),缓存仍保留但不再是关键路径。
CI 已经真正编出了 HAP(hvigor BUILD SUCCESSFUL,64,997,510 B),但校验步骤拿 package.json 的完整版本 `2.0.2-Beta.1` 去比 module.json 里的 `2.0.2`—— 打包脚本为了让 AppGallery 接受版本号,写进 HAP 的是纯数字前缀。 校验端改成同样的截断规则(ci.yml 与 release-harmony.yml 各一处)。
CI 在「Generate checksums and verify」步骤 exit 1 但**一行输出都没有**:我上次给
Release 增加 `SHA256SUMS-linux` 时,把这些副本放在校验之前复制,而
`verify-linux-egui-packages.sh` 里有一条
test "$(find "$OUTPUT" -maxdepth 1 -type f | wc -l)" -eq 3
—— 4 个文件让它静默失败(`test` 不打印任何东西)。
改成:
- 允许可选的第 4 个文件 `SHA256SUMS-linux`;
- 数量不符时打印期望值、实际值和目录清单,下次不会再出现无输出的红。
6 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
补齐Linux的缺失功能,暂未完成