R8 has broken release APKs here before by stripping the JNI-loaded player and security classes, and the only way to reproduce that was to build with the real signing key and clobber the install you actually use. `./scripts/build-and-deploy.sh release --device --debug` now builds a fully minified release APK — exactly what ships — into the .debug applicationId slot, signed with the local debug keystore: release com.dtourolle.jellytau 0.5.5 release --debug com.dtourolle.jellytau.debug 0.5.5-debug-release debug com.dtourolle.jellytau.debug 0.5.5-debug It shares the applicationId *and* the signature with the plain debug build, so the two replace each other cleanly rather than colliding, and the versionName suffix says which is currently installed. No real key is needed, so the side-by-side path deliberately skips write-keystore-properties.sh. The flag reaches Gradle as JT_SIDE_BY_SIDE=1. CI never sets it, and the release manifest merges byte-identical without it — verified both ways through processUniversalReleaseMainManifest. deploy-android.sh and build-and-deploy.sh learned the flag too, since the APK path is unchanged but the package to launch is not.
118 lines
4.3 KiB
Bash
Executable File
118 lines
4.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# Build Android APK
|
|
|
|
set -e
|
|
|
|
# Source Rust environment
|
|
source "$HOME/.cargo/env.fish" 2>/dev/null || source "$HOME/.cargo/env" 2>/dev/null || true
|
|
|
|
# Set Android environment variables
|
|
export ANDROID_HOME="$HOME/Android/Sdk"
|
|
export NDK_HOME="$ANDROID_HOME/ndk/$(ls "$ANDROID_HOME/ndk" | head -1)"
|
|
|
|
echo "🤖 Building Android APK..."
|
|
echo "Android SDK: $ANDROID_HOME"
|
|
echo "NDK: $NDK_HOME"
|
|
echo ""
|
|
|
|
# Parse args: build type (debug/release) and optional --clean flag.
|
|
# By default the build is INCREMENTAL — Cargo and Vite reuse their caches.
|
|
# Pass --clean (or CLEAN=1) to wipe all caches for a from-scratch build.
|
|
#
|
|
# ABI selection: by default Tauri builds all four ABIs (arm64/arm/x86/x86_64),
|
|
# which is what a distributable universal APK needs — but for an on-device test
|
|
# it means three wasted Rust compiles. Pass --device (or ABI=aarch64) to build
|
|
# only the connected device's architecture; --abi <t> targets one explicitly.
|
|
#
|
|
# Side-by-side: the `debug` build type always installs as
|
|
# com.dtourolle.jellytau.debug ("JellyTau Debug"), so it never collides with a
|
|
# real install. `release --debug` puts a *release* build — R8-minified, exactly
|
|
# what ships — into that same slot, signed with the local debug keystore. That
|
|
# is how you validate minification (R8 stripping JNI-loaded classes has broken
|
|
# release APKs here before) without the real signing key and without
|
|
# uninstalling the app you actually use.
|
|
BUILD_TYPE="debug"
|
|
CLEAN="${CLEAN:-0}"
|
|
ABI="${ABI:-}"
|
|
SIDE_BY_SIDE="${SIDE_BY_SIDE:-0}"
|
|
next_is_abi=0
|
|
for arg in "$@"; do
|
|
if [ "$next_is_abi" = "1" ]; then
|
|
ABI="$arg"
|
|
next_is_abi=0
|
|
continue
|
|
fi
|
|
case "$arg" in
|
|
--clean) CLEAN=1 ;;
|
|
--abi) next_is_abi=1 ;;
|
|
--device) ABI="device" ;;
|
|
--debug|--side-by-side) SIDE_BY_SIDE=1 ;;
|
|
debug|release) BUILD_TYPE="$arg" ;;
|
|
esac
|
|
done
|
|
|
|
# The debug build type is side-by-side unconditionally; the flag only means
|
|
# something for a release build.
|
|
if [ "$BUILD_TYPE" = "debug" ]; then
|
|
SIDE_BY_SIDE=1
|
|
fi
|
|
|
|
# Resolve --device to the attached device's Rust target triple.
|
|
if [ "$ABI" = "device" ]; then
|
|
device_abi="$(adb shell getprop ro.product.cpu.abi 2>/dev/null | tr -d '\r\n')"
|
|
case "$device_abi" in
|
|
arm64-v8a) ABI="aarch64" ;;
|
|
armeabi-v7a) ABI="armv7" ;;
|
|
x86_64) ABI="x86_64" ;;
|
|
x86) ABI="i686" ;;
|
|
*)
|
|
echo "⚠️ Could not detect device ABI (got '${device_abi:-none}') — building all targets."
|
|
ABI=""
|
|
;;
|
|
esac
|
|
[ -n "$ABI" ] && echo "🎯 Device ABI $device_abi → building only '$ABI'"
|
|
fi
|
|
|
|
TARGET_ARGS=()
|
|
if [ -n "$ABI" ]; then
|
|
TARGET_ARGS=(--target "$ABI")
|
|
fi
|
|
|
|
# Step 0: Optionally clear build caches for a fully fresh build.
|
|
if [ "$CLEAN" = "1" ]; then
|
|
echo "🧹 Clearing build caches (clean build)..."
|
|
rm -rf node_modules/.vite dist .svelte-kit .next build target src-tauri/target 2>/dev/null || true
|
|
npm install > /dev/null 2>&1
|
|
fi
|
|
|
|
# Step 1: Sync Android source files
|
|
echo "🔄 Syncing Android sources..."
|
|
./scripts/sync-android-sources.sh
|
|
|
|
# Step 2: Build the frontend first to avoid dev server issues
|
|
echo "🎨 Building frontend..."
|
|
bun run build
|
|
|
|
# Step 2: Build Android APK
|
|
if [ "$BUILD_TYPE" = "release" ] && [ "$SIDE_BY_SIDE" = "1" ]; then
|
|
# A release build in the debug slot: R8 still runs, but the applicationId is
|
|
# suffixed and the debug keystore signs it (read by build.gradle.kts from
|
|
# JT_SIDE_BY_SIDE), so the real key is not needed and it replaces any other
|
|
# .debug install cleanly. Deliberately does NOT write keystore.properties.
|
|
echo "📦 Building side-by-side release APK (com.dtourolle.jellytau.debug)..."
|
|
JT_SIDE_BY_SIDE=1 bun run tauri android build --apk true "${TARGET_ARGS[@]}"
|
|
elif [ "$BUILD_TYPE" = "release" ]; then
|
|
# Configure release signing from .env (single source of truth). Must run
|
|
# after sync-android-sources.sh, since gen/android is (re)generated there.
|
|
./scripts/write-keystore-properties.sh
|
|
echo "📦 Building release APK..."
|
|
bun run tauri android build --apk true "${TARGET_ARGS[@]}"
|
|
else
|
|
echo "📦 Building debug APK..."
|
|
bun run tauri android build --apk true --debug "${TARGET_ARGS[@]}"
|
|
fi
|
|
|
|
echo ""
|
|
echo "✅ APK build complete!"
|
|
echo "📱 APK location: src-tauri/gen/android/app/build/outputs/apk/"
|