Files
cmshoppe/docs/tasks/T-617.md
T

3.6 KiB

id, title, phase, deps, status, created
id title phase deps status created
T-617 独立更新器事务替换与失败恢复 8
T-615
TODO 2026-07-13

问题 / 背景

Windows正在运行的 cmshopee.exe 和PyInstaller _internal/ 可能被锁定,主程序不能安全覆盖自身。必须使用运行在安装目录之外的独立更新器,在旧主程序退出后执行完整目录切换;替换中任何失败都要恢复旧程序,不能留下半套版本。

方案

  1. 新增最小化 app/updater_entry.py 与独立PyInstaller配置,构建无控制台、尽量仅依赖标准库的 cmshopee-updater.exe。它作为发布包程序文件存在,但执行前复制到 %TEMP%/cmshopee-update-<随机值>/,避免锁住安装目录或暂存目录。
  2. 主程序以后通过结构化 update-plan.json 启动更新器,传入父进程PID、安装根、已验证暂存根、目标版本、事务ID和日志路径。更新器必须重新验证所有路径:绝对路径、安装根可写、暂存根属于 .cmshopee-update/staging、版本和manifest匹配;不得信任任意命令行目标。
  3. 更新器有上限地等待主程序退出,并使用互斥锁/事务锁避免两个更新器或另一个cmshopee实例同时操作同一安装目录。启动更新器使用Windows隐藏进程标志,不弹黑色控制台窗口。
  4. 替换采用根项目事务,不逐文件覆盖 _internal/:先将旧 cmshopee.exe、整个 _internal/、版本/说明/manifest/更新器移动到 .cmshopee-update/backup/<旧版本>-<事务ID>/,再把暂存中的对应根项目移动到安装根。这样新版删除的旧依赖不会残留。
  5. data/、.cmshopee-update/ 和安装根未知文件永不进入替换清单;更新器不得扫描后删除未知目录。包内清单若声明这些排除项,立即拒绝。
  6. 每一步写事务journal。移动、权限、文件锁或启动新版失败时,按journal逆序移走不完整新版并恢复旧根项目;恢复失败要保留备份和中文日志,不得继续反复覆盖。
  7. 替换成功后启动新 cmshopee.exe 并传入事务/目标版本标识。T-617只确认新进程已成功创建;启动健康确认和早期崩溃回滚由T-619完成。
  8. 修改构建脚本和发布包,将 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业务数据或蝦皮流程。

执行记录

(完成后记录实现、验证命令与结果。)