Files
zyt/app/research/release_packaging_final_audit.md
T
2026-08-11 09:12:51 +08:00

231 lines
12 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.
# 发布打包最终只读复审
日期:2026-08-11Asia/Shanghai
目标:判断是否可以**从当前工作树**重建 DoctorWorkstation 发布包。
边界:未执行完整 PyInstaller,未修改应用/打包源码;仅新增本复审报告。
## 最终结论
**结论:当前工作树已具备启动原生完整重建的条件,未发现构建前 packaging 代码级 P0。**
- Windows 当前工作树可进入 `Build_DoctorWorkstation.bat` 完整重建;根发布入口的 `-ValidateOnly` 已通过。
- macOS 的 9 个操作入口在 Git index 中现已全部为 `100755`,入口合同会同时检查文件系统 `-x` 和 Git index mode,不再被 Windows/NTFS 的可执行位模拟掩盖。
- spec 已显式收集 `PySide6.QtMultimedia`/`QtMultimediaWidgets`,并安装独立 PyInstaller runtime hook。
- Windows/macOS build 均先检查冻结媒体文件,再依次执行 `--media-smoke-test``--smoke-test`package 仅在 build 成功后归档和计算哈希。
- README、packaging README 与 `.env.example` 已说明 build/package 区别、双媒体门禁、macOS mode 以及 `.env` 不进入发布包的部署策略。
本结论只表示“可以开始原生重建”,不表示两个平台产物已经生成或可以立即对外发布。旧 `dist` 仍是修复前成品,必须由新构建替换;macOS `.app` 仍只能在原生 macOS 上产出和验收。
## P0:无
按本轮定义——是否能从**当前本地工作树**发起完整重建——未发现 P0。
特别澄清:`src/doctor_workstation/ui/diagnosis_media.py``packaging/runtime_media_smoke.py``tests/test_packaging_media_gate.py` 等文件目前未跟踪,但它们真实存在于当前工作树,spec/测试/发布入口都能读取,因此**不是本地工作树构建阻断**。它们的未提交状态属于跨机和后续复现风险,列为 P1,而不是本轮 P0。
## P1:完整发布前仍须完成
### P1-1:必须运行新的原生完整构建,旧 `dist` 不得作为验收证据
当前 `dist/DoctorWorkstation` 和版本 ZIP 早于 QtMultimedia 修复,仍缺少新媒体模块/插件。根“一键运行”会优先启动这个旧 EXE,所以完整重建前不要用它判断当前代码。
Windows 必须重新运行:
```powershell
.\Build_DoctorWorkstation.bat
```
只有控制台依次报告以下门禁通过,且随后生成新 ZIP/哈希,才算 Windows 重建成功:
1. QtWebEngine helper/resources 与 TRTC companion 文件检查;
2. `Frozen Qt multimedia file gate passed`
3. `Frozen Qt multimedia smoke gate passed (--media-smoke-test, ...)`
4. `Frozen application entry smoke gate passed (--smoke-test, ...)`
5. `Windows package complete`
本复审按约束未运行完整 PyInstaller,因此不能提前宣称这些冻结态门禁已经在新产物上通过。
### P1-2:macOS 必须在原生目标架构主机完成构建
Windows 只能验证 shell 语法和合同,不能交叉产生 `.app`、macOS framework、媒体插件或有效签名。必须在 arm64 或 x86_64 macOS 构建机执行:
```bash
CI=1 DOCTOR_NONINTERACTIVE=1 bash scripts/check_macos_entrypoints.sh
./一键打包.command
```
成功证据必须包括媒体文件 gate、两个冻结 smoke gate、`codesign --verify`、实际架构、版本 ZIP 与 `.sha256`。这是一项平台执行门禁,不是当前脚本缺陷。
### P1-3:当前工作树尚未形成可跨机复现的发布 commit/tag
当前本机直接构建会包含未跟踪媒体源码和 runtime hook,因此可以构建;但新的 clone、CI runner 或另一台 macOS 机器不会从当前 HEAD 获得这些文件。当前还有多处修改、staged mode 变化和大量测试产物。
因此:
- 内部本机技术构建可以现在开始;
- 跨机 macOS 构建或正式发布前,发布负责人仍应审阅 intended source,提交必要文件及 9 个 mode 变化,并从确定的 commit/tag 构建;
- 不应使用 `git add -A``.pytest-tmp-*``artifacts/` 等临时产物一并纳入。
这是正式发布的可复现性 P1,但按根任务要求不作为“当前工作树本地重建”P0。
### P1-4:生产签名、公证链仍是发布策略门禁
- Windows package 生成 ZIP 与 SHA-256,但没有集成 Authenticode。
- macOS 接受可选 `MACOS_CODESIGN_IDENTITY` 并运行 `codesign --verify`,但不自动 notarize/staplead-hoc 签名也可能通过完整性验证。
内部 QA 包可以明确标记为内部构建。生产/院外分发则必须在归档前完成 Windows 签名;macOS 必须完成 Developer ID、嵌套 QtWebEngine helper 检查、notarization 和 stapling。staple 会改变 `.app`,之后要重新生成最终 ZIP 与 SHA-256。
### P1-5:媒体 gate 不替代真实播放和 TRTC 准入
`--media-smoke-test` 已真实创建 Qt 媒体对象并检查 decoder backend,但没有加载具体 MP4/HLS,也不请求摄像头/麦克风或加入 TRTC 房间。正式验收仍需在最终冻结包上覆盖:
- 获准分发的 MP4,产品要求时再覆盖 HLS;
- 画面渲染与音频输出;
- QtWebEngine companion/WebChannel
- 摄像头、麦克风、扬声器切换;
- TRTC 测试房间、弱网、设备插拔和休眠恢复。
## P2:建议改进
1. 仓库仍无 `.gitattributes`,本机 `core.autocrlf=true`;当前 shell 文件是 LF 且 `bash -n` 通过,但建议固定 `*.sh`/`*.command` 为 LF。
2. Windows 使用 `uv sync --frozen`macOS 使用 `uv sync --locked`;建议补 `uv lock --check` 或统一 lock 新鲜度策略。
3. Windows `version_info.txt``0.1.0.0` 与 pyproject `0.1.0` 当前一致,但仍是人工同步,建议增加版本一致性门禁。
4. `Build_DoctorWorkstation.bat -ValidateOnly` 仍输出 `Package ready in: ...`,容易让人误以为已经生成新包;退出码和行为本身正确。
5. Windows/macOS 的哈希文件命名分别为 `SHA256SUMS.txt``<zip>.sha256`,可统一以简化发布自动化。
6. spec 尚未设置原生 `.ico`/`.icns`;不影响构建或媒体能力,但正式品牌发布可补齐。
7. `research/release_packaging_audit.md``release_packaging_gate_fixed.md` 是阶段性历史记录,其中关于旧 mode/旧产物的描述不能替代本最终复审。
8. AGENTS.md 引用的 `.trellis/workflow.md``.trellis/spec/` 在当前工作区仍不存在;属于治理缺口,不是本地构建阻断。
## 独立复审结果
### 1. PyInstaller specPASS
`packaging/doctor_workstation.spec` 现在:
- 显式 hidden import
- `PySide6.QtMultimedia`
- `PySide6.QtMultimediaWidgets`
- 继续显式收集 QtWebEngine 模块;
-`video_companion/dist` 放入 `video_companion_dist`
- 要求 `packaging/runtime_media_smoke.py` 必须存在;
-`runtime_hooks=[str(MEDIA_SMOKE_HOOK)]` 安装媒体 gate。
当前 PyInstaller 6.22 的官方 `hook-PySide6.QtMultimedia.py` 会追加 Widgets 模块,Qt6 module mapping 会收集 `plugins/multimedia`,与本 spec 的显式 imports 一致。
### 2. `--media-smoke-test`PASS
`packaging/runtime_media_smoke.py` 作为 runtime hook,在普通启动时无副作用;仅当 argv 含 `--media-smoke-test` 时才在应用入口前执行:
- 导入 `QMediaPlayer``QAudioOutput``QMediaFormat``QVideoWidget`
- 创建并连接 player/audio/video 对象;
- `player.isAvailable()` 必须为真;
- Decode 模式至少暴露一种支持格式;
- 显示 16×16 offscreen video widget,并完成一次 Qt event loop
- 任一错误固定返回 70,成功返回 0。
独立测试验证了三种状态:普通 argv 惰性退出 0;隔离掉 site-packages 后媒体 argv 返回 70;当前 PySide6 环境 offscreen 实例化返回 0。
### 3. Windows build/packagePASS(静态与轻量合同)
`scripts/build_windows.ps1` 在 PyInstaller 后要求以下文件:
- `QtMultimedia.pyd`
- `QtMultimediaWidgets.pyd`
- `Qt6Multimedia.dll`
- `Qt6MultimediaWidgets.dll`
- `plugins/multimedia/ffmpegmediaplugin.dll`
- `plugins/multimedia/windowsmediaplugin.dll`
随后先运行 `--media-smoke-test`,再运行原 `--smoke-test`;两者均沿用隔离配置目录、loopback-only proxy、offscreen、30 秒超时、退出码和未捕获异常日志检查。
`scripts/package_windows.ps1` 把 runtime hook 加入 required files,调用 build 之后才复制 release launcher、压缩 ZIP 和生成 SHA-256。`Build_DoctorWorkstation.bat -ValidateOnly` 本轮通过。
### 4. macOS build/packagePASS(静态与轻量合同)
`scripts/build_macos.sh` 在成功前要求:
- `PySide6.QtMultimedia``QtMultimediaWidgets` Python `.so`
- 对应 framework,或适配 dylib 布局的后备检查;
- `plugins/multimedia` 目录;
- 至少一个 `*mediaplugin*.dylib/.so` backend
- QtWebEngine helper/resources、companion 与主可执行文件。
之后执行 `codesign --verify`,再顺序运行媒体 smoke 和应用 smoke。`scripts/package_macos.sh` 在准备依赖前先运行入口合同,并只在 build 全部成功后用 `ditto` 归档和计算哈希。
这些路径模式与当前 PySide6/PyInstaller 的 macOS framework/plugin 布局相容;最终仍需原生构建确认。
### 5. macOS Git executable modePASS
以下 9 个文件的 index mode 本轮均为 `100755`
1. `package_macos.command`
2. `run_macos.command`
3. `一键打包.command`
4. `一键运行.command`
5. `scripts/build_macos.sh`
6. `scripts/check_macos_entrypoints.sh`
7. `scripts/macos_helpers.sh`
8. `scripts/package_macos.sh`
9. `scripts/run_macos.sh`
`scripts/check_macos_entrypoints.sh` 已将自身纳入 operational files,逐个执行 `bash -n`、检查实际 `-x`,在 Git 工作树中还读取 index mode 并要求 `100755`。本轮 Git Bash 合同执行通过。
### 6. README 与 `.env`PASS
- 根 README 正确区分 `build_*`(只生成/验证 onedir 或 `.app`)和根/package 入口(生成版本 ZIP 与哈希)。
- Windows/macOS 一键打包描述已包含 QtWebEngine/QtMultimedia 文件门禁与两个冻结 smoke。
- README 明确 macOS 操作文件必须以 mode `100755` 跟踪。
- `.env.example` 与 README 明确该文件不进入 ZIP/`.app`;生产配置应由受控 launcher/MDM/进程环境注入。
- 文档明确禁止把密码、token、UserSig、TRTC SecretKey 等凭据放进 `.env` 或发布包。
### 7. 输出合同:PASS(待新构建兑现)
输出命名未被修复破坏:
- Windows onedir`dist/DoctorWorkstation/`
- Windows ZIP`dist/DoctorWorkstation-Windows-x64-<version>.zip`
- Windows checksum`dist/SHA256SUMS.txt`
- macOS bundle`dist/DoctorWorkstation.app`
- macOS ZIP`dist/DoctorWorkstation-macOS-{arm64|x64}-<version>.zip`
- macOS checksum`<zip>.sha256`
两个 package 流程都位于 build/gate 之后,失败不会进入新的归档步骤。
## 本轮实际执行的轻量检查
| 检查 | 结果 |
|---|---:|
| Windows 三份 PowerShell parser | PASS |
| `Build_DoctorWorkstation.bat -ValidateOnly` | PASS |
| 全部 macOS shell/command `bash -n` | PASS |
| `scripts/check_macos_entrypoints.sh`(含 index mode | PASS |
| packaging media gate + one-click + entrypoint pytest | **10 passed** |
| runtime hook/packaging test Ruff | PASS |
| relevant tracked diff whitespace check | PASS |
pytest 首次执行曾因沙箱无权枚举系统 `pytest-of-pc` 临时目录而产生 1 个 setup error;改用工作区内独立临时目录重跑后 10 项全部通过,并已安全清理该目录。该环境错误不是代码失败。
## 主代理下一步
### 当前 Windows 工作树
```powershell
.\Build_DoctorWorkstation.bat -ValidateOnly
.\Build_DoctorWorkstation.bat
```
完成后不要只看退出码;确认新 onedir/ZIP 时间戳、媒体文件、两个 smoke gate 日志及 SHA-256,并在无开发环境机器上做真实媒体/TRTC 验收。
### 原生 macOS
先确保目标机器拿到与当前工作树等价的全部 intended files(正式发布推荐先形成 commit/tag),然后:
```bash
git ls-files --stage -- '*.command' 'scripts/*.sh'
CI=1 DOCTOR_NONINTERACTIVE=1 bash scripts/check_macos_entrypoints.sh
export MACOS_CODESIGN_IDENTITY='Developer ID Application: <组织名称> (<TEAMID>)'
./一键打包.command
```
随后验证 `.app` 架构、媒体 framework/plugin、两个 smoke gate、签名、公证/staple、最终 ZIP 哈希和真实设备/TRTC 流程。