Files
ycode/docs/升级发布说明-2026-10-20.md

284 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# YCode 更新发布说明
> 2026-10-20 23:53(Asia/Shanghai)追加发布:线上 latest 已更新为 **0.0.7**。包含技能/MCP 输入引用、Archify 内置推荐与官方 GitHub API 固定包安装、仓库 owner 本地头像、统一详情与全局遮罩、原位安装及新任务“去使用”、斜杠原子消费和焦点恢复修正。以下旧版本条目保留为历史,不代表当前 latest。
>
> 安装包:`release/publish/0.0.7/YCode-0.0.7-Setup-x64.exe`,**237351255 字节**,SHA-256 **506768934c92979ba38c4ab922b58d2de98b590a125fd1a5dacd8dadd2ddaff3**。公开地址:`http://192.168.10.6/ycode/releases/versions/0.0.7/YCode-0.0.7-Setup-x64.exe`。
>
> 完成升级回归、发布流程17项自测、类型检查与完整构建。使用官方 checksums.json 校验过的 Electron 37.10.3 缓存 ZIP,通过命令行 electronDist 打包,未更改全局下载配置;包内版本、固定 GitHub 来源与 commit、新详情/遮罩/安装状态/Slate 删除接口、离线头像哈希和公钥一致性检查通过。先生成 Ed25519 签名清单,再以 `npm run release -- --version 0.0.7 --resume` 执行部署预检、SSH 发布与 HTTP 清单复核,历史目录不覆盖,暂存目录已清理。
>
> 正式 `0.0.7` 生产 EXE 使用隔离数据启动,真实 IPC 检查服务器返回 up-to-date;模拟旧版本通过正式更新后端实际完整下载,大小/SHA-256 一致、状态 ready,安装器适配器调用为0,真实安装文件/注册表状态未变化。报告:`.tmp/updates-live/2026-10-20T15-53-57-094Z/report.json`。已检查生产包启动截图。此验证没有执行 NSIS 安装/覆盖升级。
>
> 安装包 Authenticode 状态为 **NotSigned**,不能将 Ed25519 清单签名表述为 Windows 代码签名。日志:`.tmp/release-0.0.7-updates.log`、`.tmp/release-0.0.7-build.log`、`.tmp/release-0.0.7-package.log`、`.tmp/release-0.0.7-package-verification.json`、`.tmp/release-0.0.7-prepare.log`、`.tmp/release-0.0.7-publish.log`、`.tmp/release-0.0.7-live-verification.log`。本地版本与 lock 更新为0.0.7,未 Git 提交或打 tag,未安装或重启用户安装版。
> 2026-10-20 追加发布:线上 latest 已更新为 `0.0.6`,移除升级前的原生二次确认窗。
> 安装包为 `release/publish/0.0.6/YCode-0.0.6-Setup-x64.exe`,大小 `237189173` 字节,
> SHA-256 `7e672b4a76349fb799e6c509c2901a199e5e6bb4266dc4e78d6bffe978c33c5c`。
> 更新测试、完整构建、打包内版本/公钥/旧弹窗移除/ASAR修复检查、SSH 发布、HTTP 签名清单和完整安装包哈希复核通过。
> 首次一键执行在打包时访问 GitHub 超时;使用本机已有 Electron 37.10.3 缓存 ZIP(按 electron 包自带 checksums.json 验证 SHA-256)完成打包,
> 生成签名清单后通过 `npm run release -- --version 0.0.6 --resume` 完成发布。未修改发布脚本或全局下载配置。
> 日志:`.tmp/release-0.0.6.log`、`.tmp/release-0.0.6-package-retry.log`、`.tmp/release-0.0.6-resume.log`。
> 未执行安装器、覆盖升级或重启用户应用;下方 `0.0.5` 状态保留为历史记录。
日期:2026-10-20
状态:发布工具、服务端与推荐版本 `0.0.5` 已发布;隔离启动和真实签名下载已验收,
尚未执行真实覆盖升级验收
## 1. 当前结果
- 更新服务器:`local001`,HTTP 基址 `http://192.168.10.6/ycode/releases/`。
- 发布根目录:`/srv/ycode`;不可变版本目录:`/srv/ycode/releases/versions/`。
- SSH 暂存目录:`/srv/ycode-staging`,不对外提供 HTTP 访问。
- nginx 独立片段:`/etc/nginx/snippets/ycode-releases.conf`。
- nginx 首次变更前备份:`/var/backups/nginx/ycode-before-20261020T091317Z-default`。
- 下载页路由变更前备份:`/var/backups/nginx/ycode-before-20261020T092709Z-default`。
- `/srv/baocode` 和原有 `/baocode/` location 未改动。
- `/ycode/releases/latest.json` 当前返回 200、`Cache-Control: no-store`,版本为 `0.0.5`。
- `/ycode/releases/` 当前返回 200,显示从签名清单生成的 `0.0.5` 下载页;页面无脚本。
- 线上 `latest.json` 于 `2026-10-20T10:27:49.041Z` 发布,SHA-256 为
`d985b7b94ed9e4661eec03489e801ba41aff295b4d9ea6be8a6930edf8247245`;下载页
SHA-256 为 `42fb30160c2250ff04f23b3505a6fb0bb97aa3a1a91cb91ef4dd0cf0c314f285`。
- 推荐线上安装包实测 `237106337` 字节,SHA-256 为
`a3b8ece96077af81f9c8c676cb7d0d71f16a5d73332bc48b865c5cba7e92fd0d`。
- `0.0.2`、`0.0.3`、`0.0.4` 安装包作为不可变历史版本保留,均已被 `0.0.5` 取代。
- 发布完成后 `/srv/ycode-staging` 为空;所有访问和文件校验均已完成。
- 公钥:`resources/updates/public-key.pem`;Electron `extraResources` 会把它打包为
`resources/updates/public-key.pem`。
- 公钥 SPKI SHA-256 指纹:`320a1f9016e9fbb7097074a68e1d3d15633fe05da5cb418f448372d37a8c3ea5`。
- 私钥:`%USERPROFILE%\.ycode-release\update-signing.key`,仅当前 Windows 账户可访问,不在仓库内。
当前 `package.json` 为内部版本 `0.0.5`,安装包名为
`YCode-${version}-Setup-${arch}.${ext}`。`0.0.5` 已构建、签名清单验证并发布到
`local001`。本轮未执行安装包 Authenticode 复验,不能把该发布包声明为已做代码签名。
## 2. 清单契约
完整契约见 `resources/updates/manifest-contract.md`。
```json
{
"payload": {
"schemaVersion": 1,
"version": "0.0.5",
"publishedAt": "2026-10-20T10:00:00.000Z",
"notes": { "zh": "更新说明", "en": "Release notes" },
"platforms": {
"windows-x64": {
"url": "http://192.168.10.6/ycode/releases/versions/0.0.5/YCode-0.0.5-Setup-x64.exe",
"size": 123456789,
"sha256": "64-lowercase-hex"
}
}
},
"signature": "base64-ed25519-signature"
}
```
- Ed25519 签名覆盖且仅覆盖 `Buffer.from(JSON.stringify(payload), "utf8")`。
- payload 字段插入顺序属于签名契约,发布脚本固定为 `schemaVersion, version, publishedAt, notes, platforms`。
- SHA-256 为小写十六进制;大小是实际安装包字节数。
- 安装包 URL 必须是绝对 HTTP(S)、无查询、无片段、无凭据,并精确位于
`<base>/versions/<version>/` 下。
- 客户端只接受与清单同源的安装包 URL 和同源重定向。
- `latest.json` 禁止缓存;版本目录和安装包内容不可覆盖。
## 3. 发布命令
### 一键打包发布(推荐)
在 Windows x64 发布机的项目目录运行:
```powershell
npm run release
```
按提示输入新的正式版本号,例如 `0.0.6`,随后自动执行预检、更新本地版本、升级回归测试、
完整构建、NSIS 打包、签名清单生成、部署预检、SSH 发布与 HTTP 清单复核。默认发布到
`local001`,继续使用现有用户目录中的私钥和仓库公钥,不需要手动复制安装包或清单。
只输入版本号时会使用通用更新说明,也可以直接传参:
```powershell
npm run release -- --version 0.0.6 --notes-zh '移除升级前的二次确认窗。' --notes-en 'Remove the upgrade confirmation dialog.'
npm run release -- --help
```
- 仅接受 `x.y.z` 正式版本;新发布版本必须高于服务器 latest,且不得低于仓库版本。
- 安装包、签名清单保存在 `release/publish/<版本>/`;不会覆盖历史安装包,也不会自动安装或重启应用。
- 同步更新 `package.json`、`package-lock.json` 的项目版本,不执行 Git 提交或打 tag。
- 任一步失败即停止。本地目标版本和已有产物保留,不自动回滚服务器。
- 已完成安装包和签名清单、但发布中断时,可复用原始产物重试;不会重新打包或重新签名:
```powershell
npm run release -- --version 0.0.6 --resume
```
复用模式仍校验版本、签名、大小、哈希和安装包身份;服务器同版本必须与本地清单完全一致,
更高版本或同版本不同内容都会被拒绝。若失败时尚未生成完整签名清单,不能使用复用模式。
若已留下不完整的版本输出目录,普通模式不会覆盖它;可使用更高版本重新发布,或确认该版本
未发布后手动移走不完整产物,再重新运行同版本发布。
脚本使用本地独占锁避免重复运行;异常终止留下锁时,应先确认没有发布进程运行,再处理报错指出的锁文件。
`npm run test:release` 使用隔离的假执行器验证流程,不连接升级服务器、不实际打包或发布。
以下为手动分步命令,便于排障;通常无需执行。
先完成协调方构建,确认安装包已经包含 updater 并完成基础启动验收。不要发布当前
`release/YCode-v0.01-Setup-x64.exe`:它的 ProductVersion 是 `0.0.1`,且没有更新能力。
### 3.1 准备签名清单
```powershell
Set-Location 'C:\Users\Administrator\code\YCode'
node scripts/updates/prepare-release.mjs `
--installer 'release\YCode-0.0.5-Setup-x64.exe' `
--version '0.0.5' `
--notes-zh '修复 Electron ASAR 安装标记误判,恢复重启升级。' `
--notes-en 'Fix Electron ASAR installation-marker misclassification and restore restart-based upgrades.' `
--output '.tmp\updates\0.0.5\latest.json'
```
该命令在生成清单前强制检查:
1. 文件名安全且为 `.exe`。
2. 实际 EXE ProductVersion 等于 `--version`。
3. `package.json` 的版本等于 `--version`。
4. 版本高于无 updater 的 `0.0.1` 基线。
5. 安装包大小为 20 MiB 到 4 GiB,SHA-256 从实际文件计算。
6. 签名能被仓库公钥验证。
### 3.2 只读预检
```powershell
node scripts/updates/deploy.mjs `
--manifest '.tmp\updates\0.0.5\latest.json' `
--installer 'release\YCode-0.0.5-Setup-x64.exe' `
--transport ssh `
--ssh-host local001 `
--dry-run
```
预检会重新计算安装包哈希、验证清单签名、读取并验证服务器现有
`latest.json`,然后检查版本单调性;不会修改服务器。
### 3.3 正式发布
```powershell
node scripts/updates/deploy.mjs `
--manifest '.tmp\updates\0.0.5\latest.json' `
--installer 'release\YCode-0.0.5-Setup-x64.exe' `
--transport ssh `
--ssh-host local001
```
发布顺序:
1. 安装包和清单先上传到 `/srv/ycode-staging/<随机 ID>/`。
2. 服务端重新校验大小和 SHA-256。
3. 新版本先以目录重命名方式进入不可变 `versions/<version>/`。
4. 从已验签 payload 生成转义后的静态 `index.html`,包含版本、安装包链接、SHA-256 和中英文说明。
5. 在文件锁下再次确认现有 `latest.json` 未在发布期间变化。
6. 先原子替换 `index.html`,再原子替换 `latest.json`;最后一步失败时回滚旧页面。
任何一步失败,旧 `latest.json` 保持不变。已落盘但未成为 latest 的版本目录可以作为
同版本重试的幂等产物;若同版本目录中的包哈希不同,发布会被拒绝。
### 3.4 发布后复核
```powershell
node scripts/updates/deploy.mjs `
--manifest '.tmp\updates\0.0.5\latest.json' `
--installer 'release\YCode-0.0.5-Setup-x64.exe' `
--transport ssh `
--ssh-host local001 `
--dry-run
Invoke-WebRequest 'http://192.168.10.6/ycode/releases/latest.json' -UseBasicParsing |
Select-Object StatusCode,Headers,Content
```
## 4. 验证命令
### 4.1 发布工具自测
```powershell
node scripts/updates/selftest.mjs
node scripts/updates/contract-selftest.mjs
```
`selftest.mjs` 使用临时目录、临时密钥和本地 HTTP 服务,不上传 SSH,也不会写正式站点。
当前覆盖:
- 密钥生成、公钥格式和 Windows ACL。
- SemVer 优先级和 build metadata 规则。
- 签名精确载荷、说明/哈希/签名篡改拒绝。
- URL 查询、片段、凭据、跨版本路径、时间格式和小写哈希策略。
- 本地假传输发布、HTTP 拉取、大小与 SHA-256 复核。
- 幂等重发、同版本冲突、降级拒绝、失败前旧 latest 保持。
- 下载页文本转义、无脚本、latest/页面同步切换和失败时页面回滚。
`contract-selftest.mjs` 用 esbuild 加载 `src/main/updates/manifest.ts`,验证发布端清单能被
后端实际解析、验签和路径策略接受。
### 4.2 服务端验证
```powershell
ssh local001 "nginx -t"
Invoke-WebRequest 'http://192.168.10.6/ycode/releases/latest.json' -Method Head -UseBasicParsing
Invoke-WebRequest 'http://192.168.10.6/ycode/releases/' -UseBasicParsing
Invoke-WebRequest 'http://192.168.10.6/baocode/' -Method Head -UseBasicParsing
```
当前预期状态:YCode latest 为 200 且版本为 `0.0.5`,下载页为 200 并包含线上版本、链接和哈希,
`/baocode/` 为 403;YCode 不能开启目录浏览。
`0.0.5` 的隔离启动和真实 LAN 清单/安装包下载验证已通过:
- 脚本:`node scripts/verify-updates-live.cjs --expected-version 0.0.5`
- 结果:PASS;实际启动、签名清单校验、完整下载、大小和 SHA-256 均通过。
- 报告:`.tmp/updates-live/2026-10-20T10-28-49-489Z/report.json`。
- 产物验证:`app.asar` 内版本为 `0.0.5`,更新实现包含修复后的 ASAR 安装标记判定,打包公钥与仓库一致。
- 边界:本轮没有启动 NSIS 安装器,没有重启或修改当前用户安装,因此这不等于真实覆盖升级验收。
## 5. 首次更新基线
现有 `0.0.1` 客户端没有 updater,不能通过更新服务器升级到 `0.0.5`。
正在运行的 `0.0.3`、`0.0.4` 客户端受旧版检查器的 ASAR 安装标记误判影响,应手工安装
`0.0.5` 引导包,不能把“发现更新”视为该路径已经可用。`0.0.1` 同样需要手工安装
`0.0.5`。其他旧版客户端的真实覆盖路径仍以隔离环境复测为准。引导到 `0.0.5` 后:
```text
0.0.1(无 updater,手动安装 0.0.5)
0.0.3 / 0.0.4(检查器存在已知问题,手动安装 0.0.5)
→ 启动 0.0.5 并确认实际版本
→ 后续版本由 0.0.5 检查、下载、签名校验
→ 用户在应用中确认重启
→ NSIS 覆盖安装
→ 新版本启动后核对实际版本
```
不能把“上传成功”“清单可下载”或“客户端显示发现更新”当作端到端升级验收。至少还需要一次
隔离安装目录的真实 `0.0.4 → 0.0.5` 覆盖安装测试。
## 6. 密钥管理
- 私钥只允许存在于发布机用户目录或离线加密备份,不进入仓库、服务器、构建产物、聊天记录和日志。
- 公钥随客户端分发并作为固定信任锚。
- 当前私钥 ACL 仅授予 `WIN-JA8HHLU9216\Administrator`。
- 发布前可运行 `init-key.mjs` 的默认路径检查;路径已存在时脚本会拒绝覆盖。
- 私钥丢失或泄露时,不能用旧客户端认可的常规更新完成轮换。需要构建一个包含新公钥的
临时安装包,手工分发并安装,同时提升版本;之后再由新密钥继续发布。
- 不要为了“省事”在服务器上保留私钥,也不要将私钥复制到 `/srv/ycode`。
## 7. 已知限制
- 当前为 LAN HTTP,仅适用于能访问 `192.168.10.6` 的设备。没有 TLS、登录鉴权、限流和外部 CDN。
- 签名可防止安装包和清单被替换,但不能防止同网段攻击者阻断下载或造成拒绝服务。
- 尚未接入 HTTPS 域名、证书、灰度发布、多渠道、增量更新或正式 Authenticode 签名。
- 服务端没有回滚命令;发布后需要回退时必须发布一个更高版本,不能覆盖已发布版本目录。
- `resources/updates/public-key.pem` 已进入 Electron `extraResources`;正式包仍需做一次资源存在性验收。
- 推荐 `0.0.5` 安装包的 Authenticode 状态本轮未复验;真实性目前以 Ed25519 签名清单、
文件大小和 SHA-256 为已验证边界。
- 本轮没有执行真实 `0.0.4 → 0.0.5` 覆盖升级,也没有在独立用户目录完成完整安装/卸载验收。
- 专用 NSIS 运行时探针没有执行;安装器写入、覆盖、重启和卸载行为仍是测试缺口。