mingdao-harness 0.6.35 → 0.6.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/CODE-SIGNING.md +41 -0
- package/package.json +1 -1
- package/src/session-events.js +8 -1
package/docs/CODE-SIGNING.md
CHANGED
|
@@ -195,3 +195,44 @@ node scripts/update-sign.mjs --verify desktop/dist/mingdao-setup-<ver>-x64.exe -
|
|
|
195
195
|
|
|
196
196
|
*配置文件位置:`desktop/electron-builder.yml`(azureSignOptions 注释)、`.github/workflows/desktop.yml`(env 段)。*
|
|
197
197
|
*来源签名实现:`desktop/update-verify.js`(判据/文案单源)、`desktop/main.js`(门禁接线)、`scripts/update-sign.mjs`(发布侧工具)、`test/smoke.js` §135、`test/mutate/batch21-update-sign.mjs`。*
|
|
198
|
+
|
|
199
|
+
## 六、更新来源签名的**两版上线**规程(v0.6.37 起)
|
|
200
|
+
|
|
201
|
+
> 版本说明:v0.6.36 因 GitHub 退役 macOS 14 runner 未能发布(tag 保留、无 Release/npm/官网),
|
|
202
|
+
> 内容随 **v0.6.37** 首发;因此密钥环在 v0.6.37 到达用户。v0.6.38 是 macOS CI 测试判据的修复版
|
|
203
|
+
> (无产品行为变化),**签发继续顺延**——从 **v0.6.39** 开始。
|
|
204
|
+
|
|
205
|
+
### 6.1 为什么不能一次上线
|
|
206
|
+
|
|
207
|
+
`decideUpdatePolicy()` 对"**有签名但验不过**"是 **reject**(这是对的:攻击者换掉 `.sig` 就等于伪造来源)。
|
|
208
|
+
因此如果直接用**新私钥**签发 `.sig` 并发布,而存量客户端内置的还是**旧公钥**,它们会:
|
|
209
|
+
下载 → 取到 `.sig` → 用旧公钥验 → 失败 → **永久拒绝更新**。
|
|
210
|
+
这比"没有签名"严重得多——用户会被卡死在旧版本,只能手动重装。
|
|
211
|
+
|
|
212
|
+
### 6.2 两版
|
|
213
|
+
|
|
214
|
+
| 版本 | 代码 | 发布侧 |
|
|
215
|
+
| --- | --- | --- |
|
|
216
|
+
| **v0.6.38(本版)** | 密钥环/签名脚本/文案均沿用 v0.6.37(本版只修 macOS CI 测试判据) | **仍然不发布 `.sig`**(CI 开关关闭) |
|
|
217
|
+
| **v0.6.39(下一版)** | 不需再改代码(密钥环已能认新钥) | 用新私钥签 `.sig` → 发布到 GitHub Release **与** 站点 `/updates/<文件名>.sig`;CI 开关打开 |
|
|
218
|
+
|
|
219
|
+
### 6.3 开关与密钥
|
|
220
|
+
|
|
221
|
+
- 私钥**不入库**:`~/.mingdao/update-signing-key.pem`(0600),CI 里放 `secrets.UPDATE_SIGNING_KEY`(PEM 全文);
|
|
222
|
+
- CI 开关:仓库变量 `UPDATE_SIGNING_ENABLED=true` 才执行签名与上传(默认关闭);
|
|
223
|
+
- 生成/轮换:`node desktop/update-verify.js --keygen`(沿用既有实现,私钥不打印、拒绝入库),
|
|
224
|
+
然后把新公钥追加到 `UPDATE_PUBLIC_KEYS`(**旧钥必须保留至少一个大版本**,否则又会断掉存量客户端)。
|
|
225
|
+
|
|
226
|
+
### 6.4 发布时 `.sig` 放哪里
|
|
227
|
+
|
|
228
|
+
客户端按 **`https://harness.mingdao.ai/updates/<安装包文件名>.sig`** 取(`collectUpdateSignature` 的第三来源)。
|
|
229
|
+
所以发版脚本要把 `scripts/sign-release-assets.mjs --out <sig 目录>` 的产物同时:
|
|
230
|
+
1. 传到 GitHub Release(便于审计与镜像);
|
|
231
|
+
2. **复制到站点 `updates/` 目录**(客户端真正读取的位置)。
|
|
232
|
+
|
|
233
|
+
### 6.5 验收
|
|
234
|
+
|
|
235
|
+
- `node test/update-signing.js`(10 项):密钥环多钥、篡改必拒、签名脚本产物可验、缺钥 CLI 非零退出、文案分层;
|
|
236
|
+
- `batch77`(8 条变异)必须全中;
|
|
237
|
+
- 启用后的首个版本,必须实测"旧版客户端(0.6.35 或 0.6.37/0.6.38)能从 0.6.39 正常更新"——
|
|
238
|
+
若旧版拒绝,说明 `.sig` 已发布但旧版不在密钥环内,**立即撤下 `/updates/*.sig`**(回滚只要删文件,不动代码)。
|
package/package.json
CHANGED
package/src/session-events.js
CHANGED
|
@@ -126,7 +126,14 @@ function withAppendLock(file, fn) {
|
|
|
126
126
|
} catch {}
|
|
127
127
|
}
|
|
128
128
|
const code = String(/** @type {any} */ (err)?.code || '');
|
|
129
|
-
|
|
129
|
+
// v0.6.37:Windows 上 `openSync(lock,'wx')` 在"锁已被别人创建"时**不一定**报 EEXIST——
|
|
130
|
+
// 实测 8 进程并发会得到 `EPERM: operation not permitted, open '…concurrent.events.ndjson.lock'`
|
|
131
|
+
// (NTFS/FAT 的 O_EXCL 语义差异)。此前只把 EEXIST 当"有人持锁"→ EPERM 直接上抛,
|
|
132
|
+
// Windows 腿的 `batch76`(session-events 并发用例)因此 7/12、5 条逃逸。
|
|
133
|
+
// 处理:把 EPERM/EACCES/EBUSY 一并视为"有竞争",走同一套超时/陈旧回收/退避逻辑;
|
|
134
|
+
// 真正的权限错误仍会在超时后以明确的超时信息暴露(不会永远静默重试)。
|
|
135
|
+
const RETRYABLE = new Set(['EEXIST', 'EPERM', 'EACCES', 'EBUSY']);
|
|
136
|
+
if (!RETRYABLE.has(code)) throw err;
|
|
130
137
|
// 崩溃残留:临界区毫秒级,超过 10s 的锁不可能是活写方;二次 stat 防误删刚建的新锁。
|
|
131
138
|
if (reclaimStaleAppendLock(lock)) continue;
|
|
132
139
|
if (Date.now() > deadline) throw new Error(`获取事件追加锁超时:${lock}`);
|