Files
opendance-android/tasks/014-release-acceptance.md
zijunchiang c207e7dadd test(accept): 发布包级端到端验收(读 zip 内 APK)+ T-AND-014
`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) 解压 + 文件清单(apk / README.txt / THIRD_PARTY.md / contracts/VERSION);
  3) APK 的 AndroidManifest versionName/versionCode = 发布版本(复用
     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 与仓库逐项一致(抓「APK 用旧契约构建、快照拷了新的」)。
package-release.sh 结尾打印下一步验收命令;README 补用法。

实测(对**已发布**的 v0.2.0 资产重新下载后跑):sha256 OK、版本元数据 0.2.0/200、
ABI 恰为两个真机 ABI、两个 ABI 下都有 MediaPipe native、模型资源在、dex 命中
PoseLandmarker、apksigner 通过、契约快照与仓库一致 → 发布包级验收全部通过。

反向验证(真实负例):
  * 篡改包内 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 分钟**这条脚本替代不了,仍属设备实测项;本脚本能证明的是
「装得上、不会因打包配置而崩」。verify.sh 仍全绿。
2026-09-17 04:51:16 +08:00

64 lines
3.1 KiB
Markdown
Raw Permalink 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.
# 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
```