# T-AND-014:发布包级端到端验收(读 zip 内的 APK) - **状态**: done - **依赖**: T-AND-013(APK 版本元数据)/ T-AND-009(打包) - **文档**: v1.6 §6.3 / §7.5 / §11 - **契约**: 不涉及(验收流程) ## 为什么需要 `build/verify.sh` 验的是 `app/build/outputs/` 里**刚构建**的 APK,而用户装的是 zip 里的 `app-release.apk`。两者之间隔着打包与分发;本项目另两端都因此翻过车 (Web v0.7.4 的实体注册、Windows v0.2.1 的 PE 版本元数据),所以三端统一要求: **每次发布都要有一条读产物本身的验收**(文档 §7.5)。 ## 实现:`build/accept-release.sh` 对一个 zip(或已解压目录)跑完: | 步骤 | 检查 | |--|--| | 1 | sha256 校验(有 `SHA256SUMS` 时) | | 2 | 解压 + 文件清单(`app-release.apk` / `README.txt` / `THIRD_PARTY.md` / `contracts/VERSION`) | | 3 | **APK 的 `AndroidManifest` `versionName`/`versionCode`** = 发布版本(复用 `build/check-apk-version.sh`;安装界面与应用信息页读的就是它) | | 4 | ABI 恰为 `arm64-v8a` + `armeabi-v7a`(无模拟器 ABI),且**两个 ABI 下都有 MediaPipe native 库**(T-AND-012 崩溃根因之一) | | 5 | `assets/pose_landmarker_lite.task` 随包分发;**dex 里仍有 `poselandmarker/PoseLandmarker`**(R8 没整体裁掉) | | 6 | 有 `apksigner` 时顺带验签(可选,不影响结论) | | 7 | 包内 `contracts/VERSION` 与**仓库** `contracts/VERSION` 逐项一致(抓「APK 用旧契约构建、快照拷了新的」) | ## 实测(对**已发布**的 v0.2.0 资产,重新下载后跑) ``` [accept] sha256 校验:opendance-android-v0.2.0.zip: OK ✓ app-release.apk / README.txt / THIRD_PARTY.md / contracts/VERSION ✓ APK 版本元数据一致:0.2.0(versionCode 200) ✓ ABI 恰为 arm64-v8a + armeabi-v7a(真机 ABI) ✓ lib/arm64-v8a 下有 MediaPipe native 库 ✓ lib/armeabi-v7a 下有 MediaPipe native 库 ✓ MediaPipe 模型资源随包分发(assets/pose_landmarker_lite.task) ✓ dex 中存在 poselandmarker/PoseLandmarker(R8 未整体裁掉 MediaPipe;命中 1 个 dex) ✓ apksigner 验签通过(退出码 0) ✓ 包内契约快照与仓库 contracts/VERSION 一致 发布包级验收全部通过 ``` ## 反向验证 | 负例 | 结果 | |--|--| | 把包内 `contracts/VERSION` 的 `data-models` 改成 `0.6.0` | ✗ `data-models:包内 0.6.0,仓库 0.7.0`,退出码 1 | | 期望版本写 `0.2.1` 对 v0.2.0 的包 | ✗ `versionName 是 '0.2.0',期望 '0.2.1'` + `versionCode 是 200,期望 201` | ## 仍未覆盖(诚实声明) **装到真机跑 30 分钟**这条只能真机做,脚本无法替代。本脚本能证明的是: 包能验签安装(ABI 与 native 库齐备、模型资源在、R8 没裁掉类、版本元数据正确), 即「安装后不会因打包配置而崩」;运行时稳定性仍需设备实测。 ## 用法 ```bash bash build/accept-release.sh dist-release/opendance-android-v0.2.0.zip # 或对已解压目录:bash build/accept-release.sh dist-release/opendance-android-v0.2.0 ```