# 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)、无查询、无片段、无凭据,并精确位于
`/versions//` 下。
- 客户端只接受与清单同源的安装包 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//`。
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 运行时探针没有执行;安装器写入、覆盖、重启和卸载行为仍是测试缺口。