3.6 KiB
3.6 KiB
id, title, phase, deps, status, created
| id | title | phase | deps | status | created | |
|---|---|---|---|---|---|---|
| T-617 | 独立更新器事务替换与失败恢复 | 8 |
|
TODO | 2026-07-13 |
问题 / 背景
Windows正在运行的 cmshopee.exe 和PyInstaller _internal/ 可能被锁定,主程序不能安全覆盖自身。必须使用运行在安装目录之外的独立更新器,在旧主程序退出后执行完整目录切换;替换中任何失败都要恢复旧程序,不能留下半套版本。
方案
- 新增最小化
app/updater_entry.py与独立PyInstaller配置,构建无控制台、尽量仅依赖标准库的cmshopee-updater.exe。它作为发布包程序文件存在,但执行前复制到%TEMP%/cmshopee-update-<随机值>/,避免锁住安装目录或暂存目录。 - 主程序以后通过结构化
update-plan.json启动更新器,传入父进程PID、安装根、已验证暂存根、目标版本、事务ID和日志路径。更新器必须重新验证所有路径:绝对路径、安装根可写、暂存根属于.cmshopee-update/staging、版本和manifest匹配;不得信任任意命令行目标。 - 更新器有上限地等待主程序退出,并使用互斥锁/事务锁避免两个更新器或另一个cmshopee实例同时操作同一安装目录。启动更新器使用Windows隐藏进程标志,不弹黑色控制台窗口。
- 替换采用根项目事务,不逐文件覆盖
_internal/:先将旧cmshopee.exe、整个_internal/、版本/说明/manifest/更新器移动到.cmshopee-update/backup/<旧版本>-<事务ID>/,再把暂存中的对应根项目移动到安装根。这样新版删除的旧依赖不会残留。 data/、.cmshopee-update/和安装根未知文件永不进入替换清单;更新器不得扫描后删除未知目录。包内清单若声明这些排除项,立即拒绝。- 每一步写事务journal。移动、权限、文件锁或启动新版失败时,按journal逆序移走不完整新版并恢复旧根项目;恢复失败要保留备份和中文日志,不得继续反复覆盖。
- 替换成功后启动新
cmshopee.exe并传入事务/目标版本标识。T-617只确认新进程已成功创建;启动健康确认和早期崩溃回滚由T-619完成。 - 修改构建脚本和发布包,将
cmshopee-updater.exe纳入manifest和zip,并声明当前更新器协议版本。
验收要点
- 更新器运行于安装目录外,等待旧主程序退出后才替换,不出现黑色命令窗口。
_internal/整目录切换,新版已删除的旧依赖不会残留。- 注入任意移动失败、只读目录、文件锁、父进程不退出和新版启动失败时,旧程序根项目能恢复。
data/前后目录树与文件hash完全一致;安装根未知文件保持不变。- 非法plan、越界路径、版本/manifest不一致、并发更新器均被阻断。
- release包包含可独立运行的更新器,且不包含用户数据。
测试要求
- 新增更新器纯逻辑/子进程集成测试:临时安装树成功替换、故障注入回滚、超时、并发锁、路径越界、
data/不变。 - 更新打包测试,断言独立更新器产物、无控制台配置和manifest协议一致。
- 在Windows 10/11打包环境人工验证一次真实文件锁与自动重启;未执行时必须如实记录。
边界(不改什么)
- 不实现版本检查下载窗口;T-618负责GUI编排。
- 不做新版主窗口健康标记和失败版本熔断;T-619负责。
- 不删除用户备份,不修改CDP、AI、Excel、SQLite业务数据或蝦皮流程。
执行记录
(完成后记录实现、验证命令与结果。)