The compose services bind-mount the repo and build as root, so every artifact they leave in src-tauri/target belongs to root on the host. It accumulates: 11,124 such files had built up, enough that cargo clean and scripts/clean.sh failed with EACCES — and a plain cargo build died part-way through, because build scripts compile for the host and land in target/debug even when cross-compiling to Android. That is what blocked the device build in this batch. Restores ownership at the end of each containerised build, reading the intended owner from the checkout so no uid has to be plumbed through from the host. A no-op when not running as root, so the native build scripts call it unconditionally. Running the containers as the host uid is the tidier fix and stays open — it needs the cargo/bun cache volumes moved off /root first, which is why this is not a one-line user: directive. TRACES: | DR-213
122 lines
4.5 KiB
Bash
Executable File
122 lines
4.5 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/"
|
|
|
|
# Containerised builds run as root against a bind-mounted tree; hand the
|
|
# artifacts back to the host user. No-op when not root. See DR-213.
|
|
"$(dirname "$0")/restore-ownership.sh"
|