Files
cmbuyer/docs/tasks/T-405.md
T

100 lines
9.7 KiB
Markdown
Raw 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.
---
id: T-405
title: 采购工具 Windows 打包与运行文档
phase: 4
deps: [T-401]
status: TODO
created: 2026-08-04
vikunja_task_id: 53
context_ref: 8600c33
work_branch: task/t-405-windows-package
needs_device: false
needs_human_review: true
write_paths:
- docs/tasks/T-405.md
- client/pyproject.toml
- client/requirements-build.txt
- client/cmbuyer-client.spec
- client/scripts/build_windows.ps1
- client/scripts/verify_windows_bundle.py
- client/tests/test_windows_packaging.py
- client/README.md
- docs/03-tech-stack.md
- .gitignore
---
<!-- BEGIN VIKUNJA EXPORT id=53 synced=2026-08-04T15:16:24Z sha256=31032aac3c37c9992ba7ce04a5bb3cac406b2e8cef54106eeb9213d2c7b75e91 -->
## 问题 / 背景
T-401 完成生产接线和首次真实提交人工验收后,采购工具仍需转换为运营电脑可解压运行、可追溯到准确源码、构建环境实际依赖和可检查内容的 Windows 交付包。T-405 使用 PyInstaller 生成 onedir 目录并封装 ZIP,提供构建、运行时依赖清单、校验、运行、升级与卸载文档;打包和验收不连接 Android 设备,不执行采购、提交或付款。
## 关联需求与交互
- 功能:F-005、F-013 的 Windows 交付运行部分。
- 用户故事:US-003、US-007、US-008。
- 交互:IX-007、IX-008;保持“采购工具”名称、固定双 Tab 和安全退出语义。
- 依赖:T-401;只打包已经完成人工安全验收的生产 composition。
- 后续消费者:T-404 完整 MVP 验收。
- needs_device=false;构建、内容检查和无设备启动测试不得连接 ADB 或拼多多。
## 方案
1. 新增独立 build requirements,固定兼容的 PyInstaller 版本;运行时依赖继续只由 requirements.txt / wheel metadata 管理,不把 PyInstaller 作为运营时依赖或在应用启动时下载组件。
2. 维护显式 PyInstaller spec,生成 windowed onedir 包。入口唯一为 cmbuyer_client.app:main,只收集运行必需的 PySide6/uiautomator2/adbutils/Pillow 模块、Qt/Shiboken 运行库和已知资源;不使用任意目录收集、隐藏执行脚本或下载器。
3. PowerShell 构建脚本从仓库根解析并校验精确 client 目录、Python 3.11+ venv、干净的显式输出目录和当前 commit;只清理已验证位于 client 构建输出下的目录,不对仓库根、用户目录、LOCALAPPDATA 或未解析变量执行递归删除。
4. 构建前从已安装 wheel metadata/requirements 解析全部直接运行时依赖的规范名称和实际版本,不能只读取未锁定的需求表达式。至少显式探测 PySide6、Qt runtime、Shiboken6、uiautomator2、adbutils、Pillow;Qt 版本必须来自实际 Qt runtime,其他版本来自当前构建环境中实际解析的 distribution/runtime,而不是手写常量。
5. 构建产物为版本化 onedir 目录和其 ZIP;生成不含秘密的 manifest,记录产品/版本、git commit、Python/PyInstaller 版本、构建 UTC、入口、ZIP SHA-256,以及上述全部直接运行时依赖和 Qt/Shiboken 组件的规范名称、实际版本与探测来源。未锁依赖因此仍可追溯,但不得声称字节级可复现。
6. PyInstaller spec 必须保留校验所需的 distribution metadata/Qt runtime 事实。校验器分别从构建环境和实际 onedir/ZIP 内解析依赖集合与版本,要求和 manifest 精确一致;缺依赖、漏记直接依赖、版本不一致、无法探测或只相信 manifest 自报都必须失败,不发布 ZIP。
7. bundle 内容校验显式拒绝 vikunja.env、任何 .env/token、client-state.sqlite3/WAL/SHM、LOCALAPPDATA 状态、日志、artifacts、截图、XML、业务 evidence manifest、tests/fixture、__pycache__、源码仓库元数据和支付凭据。交付依赖 manifest 是允许的专用构建元数据,不能混入业务证据。
8. 校验器检查 ZIP 防路径穿越、文件清单、入口 EXE、关键依赖、重复/绝对路径、最大体积边界、SHA-256 和依赖版本;不得仅以 PyInstaller 退出码 0、requirements 约束或版本文件存在声称可交付。
9. 运行文档固定“解压整个目录后运行”,说明采购服务精确 loopback、ADB 路径/serial 人工配置、设备 token 安全录入、运行数据位于 %LOCALAPPDATA%/cmbuyer、日志/状态位置和常见错误。不得要求复制 vikunja.env、源码、Python 或开发 venv。
10. 升级只替换应用目录,先退出应用并保留 %LOCALAPPDATA%/cmbuyer;卸载只删除明确的应用解压目录,不自动删除状态、日志或证据。需要清理本地数据时必须另行人工确认精确路径。
11. 在脱离源码目录、未安装 Python 的 Windows 测试环境解压并启动,检查产品名、双 Tab、配置/错误提示和安全关闭;测试使用空闲/无设备配置,零 claim、零 ADB、零真机动作、零提交。
12. 不在 MVP 内实现安装器、自动更新、代码签名服务或后台常驻。若产物未签名,文档如实说明 Windows 提示与人工核验 SHA-256、commit 和依赖版本 manifest 的步骤,不能诱导绕过安全软件。
## 验收要点
- 构建脚本在合规 Windows venv 生成唯一 onedir ZIP 和 manifest;manifest 枚举 wheel metadata/requirements 中全部直接运行时依赖的实际版本,并至少包含 PySide6、Qt、Shiboken6、uiautomator2、adbutils、Pillow、Python、PyInstaller 与 git commit。
- 构建环境探测结果、实际 onedir/ZIP 内 distribution metadata/Qt runtime 和 manifest 三方逐项一致;漏项、未知项、版本漂移、无法从 bundle 复核或伪造 manifest 均使校验失败。
- 重复构建记录各自 commit、完整实际依赖版本集合和产物 SHA-256;依赖未锁时仍能追溯本次解析结果,但不声称字节级可复现。
- 内容负例向 staging 注入 env/token、SQLite/WAL、日志、PNG/XML、tests/fixture、绝对/穿越路径时校验均失败且不发布 ZIP。
- ZIP 解压后目录外无写入;应用所有状态仍进入 %LOCALAPPDATA%/cmbuyer,不污染安装目录或源码树。
- 在无源码、无 Python 的 Windows 环境启动成功;产品名/双 Tab/配置错误/关闭路径可用,且无设备时零 claim、零 ADB、零提交、零付款。
- 文档覆盖构建、SHA-256/commit/依赖版本核验、解压运行、配置、升级、卸载、日志和故障排查;明确系统只创建待付款订单且不付款。
- 运行 client unittest、compileall、wheel metadata、打包专用测试、完整 init、上下文、Vikunja export 与 diff-check;记录产物绝对路径与 SHA-256,但不把 ZIP、manifest 中的本机秘密或构建目录提交 Git。
- needs_human_review:运营电脑人工检查解压、启动、文档、Windows 安全提示、SHA-256 和依赖版本 manifest;确认前保持 DOING。
## 执行记录
(暂无)
<!-- END VIKUNJA EXPORT -->
## 边界
- 只交付 PyInstaller `windowed onedir` 目录及其版本化 ZIP,不在本任务引入安装器、自动更新、后台
常驻、下载器或代码签名服务。未签名时必须如实说明 Windows 提示和 SHA-256 人工核验步骤,
不得指导绕过安全软件。
- PyInstaller 只作为固定版本的构建依赖,不能进入运营时依赖或由应用启动时联网安装。运行时依赖
仍以 `requirements.txt` / wheel metadata 为唯一声明来源,不维护第二份手写依赖清单;manifest
必须记录本次构建环境实际解析并打入 bundle 的版本,不能只复制未锁定的版本约束。
- bundle 和 ZIP 绝不能包含 `vikunja.env`、任何 `.env`/token、设备/claim 凭据、本地 SQLite/
WAL/SHM、配置状态、日志、artifacts、截图、XML、证据 manifest、tests/fixture、源码仓库元数据、
支付凭据或本机绝对路径。发现拒绝项必须失败且不发布产物。
- 构建脚本只能清理已经解析并验证位于 `client` 构建输出目录下的精确路径;不得对仓库根、用户目录、
`%LOCALAPPDATA%`、空变量、通配目标或未经验证的计算路径执行递归删除。
- 交付 manifest 必须记录产品/版本、git commit、Python/PyInstaller、UTC、入口、产物 SHA-256,
以及 wheel metadata/requirements 中**全部直接运行时依赖**的规范名称、实际解析版本和探测来源;
至少显式包含 `PySide6`、实际 Qt runtime、`Shiboken6`、`uiautomator2`、`adbutils`、`Pillow`。
Qt 必须从实际 runtime 探测,其余必须来自当前构建 distribution/runtime,不能使用手写常量。
- 校验器必须分别从构建环境和实际 onedir/ZIP 内的 distribution metadata、Qt runtime/组件文件核对
完整依赖集合与版本,并要求与 manifest 精确一致。漏项、版本漂移、无法从 bundle 复核或只信任
manifest 自报都必须失败且不发布 ZIP;PyInstaller spec 必须保留完成该核对所需的最小 metadata。
- manifest 不记录 token、设备 serial、用户名、业务数据或证据路径。依赖未锁时,实际版本集合用于
追溯本次构建,但仍不得宣称字节级可复现;版本号或文件名不能替代 SHA-256/commit 证明。
- 运行、升级和卸载不得自动删除 `%LOCALAPPDATA%/cmbuyer`。升级只替换明确的应用目录;清理状态、
日志或证据必须另行由人确认精确路径,不能作为卸载脚本副作用。
- 构建与验收 `needs_device: false`:不得连接 ADB、启动拼多多、领取真实任务、执行页面动作、提交
订单或付款。无源码/无 Python 环境的启动验收使用空闲配置,保持零 claim、零真机动作。
- 本任务不修改 T-401 提交安全逻辑,不增加支付、免密支付、先用后付或任何扣款能力。
`needs_human_review: true`,运营电脑人工核验 ZIP、文档、启动和 SHA-256 前不得标 `DONE`。