#!/bin/bash # ============================================================ # OpenDance Studio —— Android 辅助 App 一键验证 # # 依次执行:单元测试 → debug 构建 → release 构建 → 产物校验 # 任一环节失败即退出非 0。 # # 用法: # JAVA_HOME=/path/to/jdk17 ANDROID_HOME=/path/to/sdk bash build/verify.sh # ============================================================ set -uo pipefail PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" cd "$PROJECT_DIR" FAILED=0 echo "============================================================" echo " 1/3 单元测试(JVM,无需设备)" echo "============================================================" if ./gradlew testDebugUnitTest --console=plain -q 2>&1 | tail -20; then echo "✓ 单元测试通过" else echo "✗ 单元测试失败" FAILED=1 fi echo " 测试报告:app/build/reports/tests/testDebugUnitTest/index.html" echo "" echo "============================================================" echo " 2/3 构建 debug + release APK" echo "============================================================" if bash build/build-apk.sh all 2>&1 | grep -vE "^> Task|^$" | tail -20; then : else FAILED=1 fi echo "" echo "============================================================" echo " 3/3 产物校验(签名 / 版本元数据 / 包信息 / 模型资源 / ABI / R8)" echo "============================================================" SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}" # 优先选用与 compileSdk 匹配的 build-tools,缺失时退回最新版 if [ -d "$SDK/build-tools/34.0.0" ]; then BT="$SDK/build-tools/34.0.0" else BT="$(ls -d "$SDK"/build-tools/* 2>/dev/null | sort -V | tail -1)" fi RELEASE_APK="app/build/outputs/apk/release/app-release.apk" if [ ! -f "$RELEASE_APK" ]; then echo "✗ 未生成 $RELEASE_APK" FAILED=1 elif [ -z "$BT" ]; then echo "✗ 未找到 build-tools,无法校验" FAILED=1 else # 以**退出码**为准:不同 build-tools 版本的 apksigner 输出文案不一致 # (34.0.0 会打印 "Verifies",36.0.0 配 --print-certs 时只打印证书信息), # 依赖日志字符串判断会导致误判。 "$BT/apksigner" verify "$RELEASE_APK" > /tmp/apk_verify.txt 2>&1 VERIFY_STATUS=$? "$BT/apksigner" verify --print-certs "$RELEASE_APK" > /tmp/apk_sign.txt 2>/dev/null if [ "$VERIFY_STATUS" -eq 0 ]; then echo "✓ 签名校验通过(apksigner 退出码 0)" grep -E "Signer #1 certificate DN" /tmp/apk_sign.txt | sed 's/^/ /' || true if grep -q "APK Signature Scheme v2" /tmp/apk_verify.txt 2>/dev/null; then echo " v2 签名方案:已启用" fi else echo "✗ 签名校验失败(apksigner 退出码 $VERIFY_STATUS)" cat /tmp/apk_verify.txt FAILED=1 fi "$BT/aapt2" dump badging "$RELEASE_APK" 2>/dev/null | head -1 | sed 's/^/ /' # ---- 版本元数据必须落在**产物本身**上(文档 §11.2)---- # gradle 的 verifyReleaseVersion 只比构建配置;APK 的 AndroidManifest 才是 # 安装界面 / 应用信息页读到的版本。构建缓存或拷错文件都只会在这一层暴露。 if bash "$PROJECT_DIR/build/check-apk-version.sh" "$RELEASE_APK"; then : else echo "✗ APK 的 versionName/versionCode 与 project.yaml 不一致" FAILED=1 fi if unzip -l "$RELEASE_APK" | grep -q "assets/pose_landmarker_lite.task"; then echo "✓ MediaPipe 模型已随包分发" else echo "✗ APK 中缺少 MediaPipe 模型" FAILED=1 fi # ---- T-AND-012:ABI 必须恰为真机 ABI,且每个 ABI 下都有 MediaPipe native 库 ---- ABIS=$(unzip -l "$RELEASE_APK" | grep -oE "lib/[^/]+/" | sed 's#lib/##;s#/##' | sort -u) ABIS_ONE_LINE=$(echo "$ABIS" | tr '\n' ' ') echo " 包含 ABI:$ABIS_ONE_LINE" EXPECTED_ABIS=$(printf 'arm64-v8a\narmeabi-v7a') if [ "$ABIS" = "$EXPECTED_ABIS" ]; then echo "✓ release ABI 恰为 arm64-v8a + armeabi-v7a(真机 ABI)" else echo "✗ release ABI 与预期不符(预期 arm64-v8a / armeabi-v7a)" FAILED=1 fi if echo "$ABIS" | grep -qE "x86"; then echo "✗ release APK 含模拟器 ABI,会导致包体翻倍且与 abiFilters 不符" FAILED=1 fi for abi in arm64-v8a armeabi-v7a; do if unzip -l "$RELEASE_APK" | grep -q "lib/$abi/.*mediapipe"; then echo "✓ lib/$abi 下有 MediaPipe native 库" else echo "✗ lib/$abi 下缺少 MediaPipe native 库(真机安装后必然初始化失败)" FAILED=1 fi done # ---- T-AND-012:R8 不得裁掉 MediaPipe(seeds 由 -printseeds 输出) ---- SEEDS="app/build/outputs/mapping/release/seeds.txt" if [ -f "$SEEDS" ]; then MP_SEEDS=$(grep -c "^com.google.mediapipe" "$SEEDS" || true) if [ "$MP_SEEDS" -gt 0 ]; then echo "✓ R8 保留 MediaPipe 类 $MP_SEEDS 条(seeds.txt)" else echo "✗ R8 把 MediaPipe 整体裁掉了,release 包必然崩溃" FAILED=1 fi else echo "✗ 未生成 $SEEDS(proguard-rules.pro 的 -printseeds 是否被移除?)" FAILED=1 fi # ---- T-AND-012:关键类必须真的在 dex 里 ---- if unzip -p "$RELEASE_APK" classes.dex 2>/dev/null | grep -qa "poselandmarker/PoseLandmarker"; then echo "✓ dex 中存在 PoseLandmarker 类" else echo "✗ dex 中找不到 PoseLandmarker(R8/打包异常)" FAILED=1 fi fi echo "" if [ "$FAILED" -ne 0 ]; then echo "============================================================" echo " 验证失败" echo "============================================================" exit 1 fi echo "============================================================" echo " 验证全部通过" echo "============================================================"