faefca0445
Android Release / build-apk (push) Successful in 3m44s
aapt2 --version-code/--version-name only inject when the attribute is absent — they silently no-op when the manifest already has a value. Removed both attributes from AndroidManifest.xml so the CI flags take effect. Local debug builds fall back to code=1 / name=0.0.0-dev. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
156 lines
5.9 KiB
Bash
Executable File
156 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build a self-signed Android APK from solitaire_app's cdylib targets.
|
|
#
|
|
# Replaces the cargo-apk pipeline with explicit cargo-ndk + aapt2 + apksigner
|
|
# steps. The CI runner was hitting an SDK-discovery bug inside cargo-apk's
|
|
# ndk-build crate that we couldn't isolate; running each Android toolchain
|
|
# step explicitly gives us a debuggable pipeline.
|
|
#
|
|
# Required environment:
|
|
# ANDROID_HOME Path to Android SDK root
|
|
# ANDROID_NDK_HOME Path to the specific NDK version
|
|
# BUILD_TOOLS_VERSION e.g. "34.0.0"
|
|
# PLATFORM e.g. "android-34"
|
|
#
|
|
# Optional environment:
|
|
# PROFILE "debug" (default) | "release"
|
|
# ABIS Space-separated Android ABIs to build (default:
|
|
# "arm64-v8a armeabi-v7a x86_64"). Reduce in CI to
|
|
# fit the runner's disk budget — a full three-ABI
|
|
# debug build can exceed 25 GB of target/ output.
|
|
# APK_OUT Output APK path (default: target/$PROFILE/apk/ferrous-solitaire.apk)
|
|
# KEYSTORE Path to keystore for signing (default: generates a debug keystore)
|
|
# KEYSTORE_PASS Keystore password (default: "android" for the generated debug keystore)
|
|
# KEY_ALIAS Key alias (default: "androiddebugkey")
|
|
# KEY_PASS Key password (default: same as KEYSTORE_PASS)
|
|
#
|
|
# Outputs:
|
|
# $APK_OUT Signed, zipaligned APK
|
|
set -euo pipefail
|
|
|
|
: "${ANDROID_HOME:?ANDROID_HOME must be set}"
|
|
: "${ANDROID_NDK_HOME:?ANDROID_NDK_HOME must be set}"
|
|
: "${BUILD_TOOLS_VERSION:?BUILD_TOOLS_VERSION must be set}"
|
|
: "${PLATFORM:?PLATFORM must be set (e.g. android-34)}"
|
|
|
|
PROFILE="${PROFILE:-debug}"
|
|
ABIS="${ABIS:-arm64-v8a armeabi-v7a x86_64}"
|
|
APK_OUT="${APK_OUT:-target/${PROFILE}/apk/ferrous-solitaire.apk}"
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
BT="$ANDROID_HOME/build-tools/$BUILD_TOOLS_VERSION"
|
|
PLATFORM_JAR="$ANDROID_HOME/platforms/$PLATFORM/android.jar"
|
|
MANIFEST="solitaire_app/android/AndroidManifest.xml"
|
|
RES_DIR="solitaire_app/res"
|
|
ASSETS_DIR="assets"
|
|
|
|
# --- sanity ----------------------------------------------------------------
|
|
for f in "$BT/aapt2" "$BT/zipalign" "$BT/apksigner" "$PLATFORM_JAR" "$MANIFEST"; do
|
|
[ -e "$f" ] || { echo "missing: $f"; exit 1; }
|
|
done
|
|
|
|
STAGING="$(mktemp -d)"
|
|
trap 'rm -rf "$STAGING"' EXIT
|
|
mkdir -p "$STAGING/lib" "$STAGING/compiled-res"
|
|
|
|
# --- 1. native libraries via cargo-ndk -------------------------------------
|
|
# `-o $STAGING/lib` lays out files as $STAGING/lib/<abi>/libsolitaire_app.so
|
|
# which is the directory structure the APK expects under lib/.
|
|
CARGO_NDK_ARGS=( --platform 26 -o "$STAGING/lib" )
|
|
for abi in $ABIS; do
|
|
CARGO_NDK_ARGS+=( -t "$abi" )
|
|
done
|
|
CARGO_NDK_ARGS+=( build --package solitaire_app --lib )
|
|
if [ "$PROFILE" = "release" ]; then
|
|
CARGO_NDK_ARGS+=( --release )
|
|
fi
|
|
echo ">>> cargo ndk ${CARGO_NDK_ARGS[*]}"
|
|
cargo ndk "${CARGO_NDK_ARGS[@]}"
|
|
|
|
# --- 2. compile + link resources and manifest ------------------------------
|
|
if [ -d "$RES_DIR" ]; then
|
|
echo ">>> aapt2 compile resources"
|
|
"$BT/aapt2" compile --dir "$RES_DIR" -o "$STAGING/compiled-res"
|
|
fi
|
|
|
|
# Derive versionCode/versionName from VERSION_NAME env var (e.g. "v0.28.0" → code 2800, name "0.28.0").
|
|
# AndroidManifest.xml intentionally has no versionCode/versionName — aapt2's --version-* flags only
|
|
# inject when absent, so the manifest must be clean for CI injection to work. Local debug builds
|
|
# fall back to code=1 / name="0.0.0-dev".
|
|
if [ -n "${VERSION_NAME:-}" ]; then
|
|
VN="${VERSION_NAME#v}"
|
|
IFS='.' read -r _MAJ _MIN _PAT <<< "$VN"
|
|
VERSION_CODE=$(( ${_MAJ:-0} * 10000 + ${_MIN:-0} * 100 + ${_PAT:-0} ))
|
|
else
|
|
VERSION_CODE=1
|
|
VERSION_NAME="0.0.0-dev"
|
|
fi
|
|
|
|
LINK_ARGS=(
|
|
link
|
|
-o "$STAGING/app-unsigned.apk"
|
|
-I "$PLATFORM_JAR"
|
|
--manifest "$MANIFEST"
|
|
)
|
|
[ -n "$VERSION_CODE" ] && LINK_ARGS+=( --version-code "$VERSION_CODE" )
|
|
[ -n "${VERSION_NAME:-}" ] && LINK_ARGS+=( --version-name "${VERSION_NAME#v}" )
|
|
[ -d "$ASSETS_DIR" ] && LINK_ARGS+=( -A "$ASSETS_DIR" )
|
|
# Add compiled resources if any
|
|
shopt -s nullglob
|
|
RES_FLATS=( "$STAGING/compiled-res"/*.flat )
|
|
shopt -u nullglob
|
|
if [ ${#RES_FLATS[@]} -gt 0 ]; then
|
|
LINK_ARGS+=( "${RES_FLATS[@]}" )
|
|
fi
|
|
echo ">>> aapt2 link"
|
|
"$BT/aapt2" "${LINK_ARGS[@]}"
|
|
|
|
# --- 3. add native libraries to the APK ------------------------------------
|
|
echo ">>> bundle native libraries"
|
|
( cd "$STAGING" && zip -r -q app-unsigned.apk lib/ )
|
|
|
|
# --- 4. zipalign -----------------------------------------------------------
|
|
echo ">>> zipalign"
|
|
"$BT/zipalign" -p -f 4 "$STAGING/app-unsigned.apk" "$STAGING/app-aligned.apk"
|
|
# Free the unsigned intermediate now — apksigner reads $app-aligned.apk and
|
|
# writes $APK_OUT, and the runner's disk is tight after a multi-ABI build.
|
|
rm -f "$STAGING/app-unsigned.apk"
|
|
|
|
# --- 5. sign ---------------------------------------------------------------
|
|
if [ -z "${KEYSTORE:-}" ]; then
|
|
# Generate a deterministic debug keystore on the fly.
|
|
KEYSTORE="$STAGING/debug.keystore"
|
|
KEYSTORE_PASS="${KEYSTORE_PASS:-android}"
|
|
KEY_ALIAS="${KEY_ALIAS:-androiddebugkey}"
|
|
KEY_PASS="${KEY_PASS:-$KEYSTORE_PASS}"
|
|
echo ">>> generating debug keystore at $KEYSTORE"
|
|
keytool -genkeypair -v \
|
|
-keystore "$KEYSTORE" \
|
|
-storepass "$KEYSTORE_PASS" \
|
|
-alias "$KEY_ALIAS" \
|
|
-keypass "$KEY_PASS" \
|
|
-keyalg RSA -keysize 2048 -validity 10000 \
|
|
-dname "CN=Android Debug,O=Android,C=US" > /dev/null
|
|
fi
|
|
|
|
KEYSTORE_PASS="${KEYSTORE_PASS:-android}"
|
|
KEY_ALIAS="${KEY_ALIAS:-androiddebugkey}"
|
|
KEY_PASS="${KEY_PASS:-$KEYSTORE_PASS}"
|
|
|
|
mkdir -p "$(dirname "$APK_OUT")"
|
|
echo ">>> apksigner sign -> $APK_OUT"
|
|
"$BT/apksigner" sign \
|
|
--ks "$KEYSTORE" \
|
|
--ks-pass "pass:$KEYSTORE_PASS" \
|
|
--ks-key-alias "$KEY_ALIAS" \
|
|
--key-pass "pass:$KEY_PASS" \
|
|
--out "$APK_OUT" \
|
|
"$STAGING/app-aligned.apk"
|
|
|
|
echo ">>> verify"
|
|
"$BT/apksigner" verify --verbose "$APK_OUT"
|
|
|
|
echo ">>> done: $APK_OUT"
|