Files
cmautobuy/docs/task/93-client在线更新与安全回退.md
T

4.0 KiB
Raw Blame History

93 Client:实现安全的在线检查、下载和下次启动更新

  • 类型:需求
  • 父级大工单:#1
  • 所属 MVP / 版本:MVP 后续 / 在线更新
  • 状态:已完成,用户验收通过
  • 日期:2026-08-10
  • Gitea 工单:#93

背景与目标

#92 已建立 Launcher.exe + app/ + data/ 发布结构,但 Client 仍需人工取得并 替换更新包。本任务增加用户主动检查和下载更新的能力,并保证更新不阻塞界面、 不覆盖本地数据,也不强制中断正在执行的采集或采购任务。

最终方案

  • 设置页显示当前版本、HTTPS 清单地址、检查按钮和稳定的更新状态。
  • 检查和下载使用 QObject + moveToThread,发现新版本后先由用户确认。
  • 清单地址只允许不含凭据、查询参数和片段的 HTTPS URL;更新包必须同源,并校验 大小、SHA256、版本和 ZIP 路径。
  • 更新包安全暂存到 data/update/app.new,不会直接替换运行中的程序。
  • 下次通过 Launcher.exe 启动时才替换 app,旧版保留为 app.old;移动失败或 新版本未通过健康检查时恢复旧版。
  • Client 版本升级为 0.2.0。由于 0.1.0 的 Launcher 不具备更新能力,第一次 升级到 0.2.0 仍须人工替换完整发布目录,从 0.2.0 起才支持在线更新。

最终实现与建单方案一致。清单 URL 额外禁止查询参数,避免 token 一类凭据随地址 写入 SQLite 或错误提示。

改了哪些

  • client/src/update_service.py:检查清单、下载校验、安全解压和待更新状态。
  • client/src/update_ui_event.py:在线更新后台 Worker、确认框、进度和生命周期。
  • client/src/settings_ui.py、settings_ui_event.py:设置页软件更新区域及事件装配。
  • client/build_tools/launcher.py:下次启动替换、运行识别、健康确认和失败回退。
  • client/src/ui_main.py、version.py:启动健康标记和 0.2.0 版本号。
  • client/test/test_update_service.py、test_update_ui_event.py、test_packaging.py: 更新服务、界面线程和 Launcher 测试。
  • docs/client/01-requirements.md、02-architecture.md、05-ui-specification.md、 06-quality-security.md:同步需求、架构、界面和安全基线。

验收结果

验收标准 结果
设置页可配置 HTTPS 清单地址,非法地址有明确提示 通过
检查和下载不阻塞主线程,且防止重复启动 通过
新版本由用户确认下载,完成后不强制重启 通过
校验大小、SHA256、版本、HTTPS 和同源 通过
拒绝越界路径、绝对路径、反斜杠路径和符号链接 通过
更新只写 data/update,本地 data 内容保持不变 通过
Launcher 保留 app.old,支持移动失败和健康失败回退 通过
已运行的主程序不会被 Launcher 替换 通过
真实下单开关和 Admin 业务接口未改变 通过
自动测试、打包和旧版升级冒烟通过 通过

测试

  • 执行的命令:
    • 在 client/ 执行 C:/Python310/python.exe -m pytest test -q --disable-warnings
    • 在 client/ 执行 .\build_client.bat
    • 在临时发布目录使用最终 Launcher.exe 执行 0.1.0 → 0.2.0 升级冒烟
  • 结果:256 项测试通过;0.2.0 发布产物生成成功;升级后当前 app 为 0.2.0, app.old 为 0.1.0,data 哨兵保留,健康标记生成,待更新状态清除。
  • 没验证到的部分:尚无真实 HTTPS 静态发布地址,未执行真实公网下载;尚未在 全新且未安装 Python 的 Windows 机器上人工验证;重复 Launcher 的提示由单元 测试覆盖,未人工点击原生提示框。

遗留问题

  • 当前 SHA256 用于发现下载损坏,不能证明发布者身份。清单签名和 Windows 代码 签名不在本工单范围内。

相关提交

  • 6d6829e feat: 实现 Client 在线更新与安全回退 (#93)