# T-WIN-017:发布包 exe 的 PE 版本资源必须等于发布版本 - **状态**: done - **依赖**: T-WIN-014(打包流程) - **文档**: v1.6 §5.3 / §5.7 / §11.2 - **契约**: 不涉及(纯打包元数据) ## 缺陷(v0.2.1 实测) 发布包 `opendance-windows-v0.2.1.zip` 里的 `OpenDance.exe`,在 Windows 上 「右键 → 属性 → 详细信息」显示的是: ``` 文件版本 0.1.0 产品版本 0.1.0 ``` 而产物名、Git tag、README 全都是 **v0.2.1** —— 正是文档 §11.2 要禁止的 「不可核对的发布」。 ### 根因 `export_presets.cfg` 里这两个键被**硬编码在 0.1.0**,且**没有任何环节读写或校验**: ```ini application/file_version="0.1.0" application/product_version="0.1.0" ``` Godot 导出时用它们(经 rcedit)写进 exe 的 PE `VERSIONINFO` 资源。 它们**不会**跟随 `project.godot` 的 `application/config/version` 自动更新 —— 于是每次只是改 `project.godot` + `project.yaml` 发版,二进制元数据一直停在 0.1.0。 `build/check-version.sh` 当时只比 `project.godot` ↔ `project.yaml`,两处一致, 所以**静态校验全绿、产物却是错的**:漂移发生在没人看的第三个位置。 ### 证据 ``` $ strings -a -el opendance-windows-v0.2.1/OpenDance.exe | sed -n '3147,3151p' FileVersion 0.1.0 ProductVersion 0.1.0 $ bash build/check-pe-version.sh 0.2.1 PE 版本资源(…/OpenDance.exe): VS_FIXEDFILEINFO FileVersion = 0.1.0.0 VS_FIXEDFILEINFO ProductVersion = 0.0.0.0 StringFileInfo FileVersion = 0.1.0 StringFileInfo ProductVersion = 0.1.0 ✗ PE FileVersion 为 0.1.0.0,期望 0.2.1.x(Windows 属性页会显示错版本) ✗ 资源字符串 FileVersion 为 '0.1.0',期望 '0.2.1' ✗ 资源字符串 ProductVersion 为 '0.1.0',期望 '0.2.1' ``` (对照组:官方模板 `windows_release_x86_64.exe` 的 `FileVersion = 4.7.2`, 说明该机上的 rcedit 改写**是生效的**,问题纯粹出在预设的值本身。) ## 修复 | 环节 | 文件 | 做法 | |--|--|--| | 同步 | `build/sync-export-version.sh`(新增) | 把版本号写进 `export_presets.cfg` 的 `application/file_version` / `application/product_version`;缺行则补行;幂等;拒绝非 `X.Y.Z` | | 导出 | `build/export-windows.sh` | **导出前**先同步;**导出后**用 `check-pe-version.sh` 直接读 exe 复核 | | 静态防线 | `build/check-version.sh` | 新增对 `export_presets.cfg` 两个键的校验(不等于项目版本即失败) | | 产物防线 | `build/check-pe-version.sh`(新增) | 直接读 exe 的 PE 版本资源:`VS_FIXEDFILEINFO`(签名 `0xFEEF04BD` 之后 16 字节,`grep -abo` 取偏移约 40 ms)+ `StringFileInfo`(`strings -el`,资源管理器属性页显示的就是这两项) | | 打包 | `build/package-release.sh` | 打包前校验源 exe、解包后再校验包内 exe(确认打包/解包没换错文件) | | 一键验证 | `build/verify.sh` | 新增步骤 1/5:已导出 exe 的 PE 版本资源;未导出则跳过并提示(打包时会强制) | | 单元测试 | `tests/test_about.gd` | 新增 `test_export_preset_version_matches_project_version`:预设两个键必须等于版本号唯一来源 | > 只校验 `VS_FIXEDFILEINFO` 是不够的:本机实测该结构里的 > `ProductVersion` 恒为 `0.0.0.0`(rcedit 只填了字符串形式),而属性页显示的是 > `StringFileInfo` 的文本值。因此两处都读、都对。 ## 验收 | 验收项 | 结果 | |--|--| | 产物 exe 的 PE 版本资源 = 发布版本 | ✅ `check-pe-version.sh` 读 v0.2.2 产物通过(FileVersion 0.2.2.0 / 字符串 0.2.2) | | 预设漂移能被拦住 | ✅ 反向验证:把预设改回 `0.1.0`,`check-version.sh` 与 `test_about.gd` 均失败 | | 打包前/解包后都校验 | ✅ `package-release.sh` 两处 `check-pe-version.sh`,失败即 `die` | | 不新增外部依赖 | ✅ 只用 `grep -abo` + `od` + `strings`(binutils),无 python/node 依赖 | ## 复现命令 ```bash cd opendance-windows bash build/check-pe-version.sh build/windows/OpenDance.exe 0.2.2 # 产物侧 bash build/check-version.sh 0.2.2 # 配置侧 bash build/sync-export-version.sh 0.2.2 # 需要修时 ```