150 lines
5.3 KiB
Bash
Executable File
150 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
ANDROID_DIR="$REPO_ROOT/platform/android"
|
|
SOURCE_APK="$ANDROID_DIR/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk"
|
|
OUT_DIR="${MAPLIBRE_VALIDATION_OUT_DIR:-$REPO_ROOT/artifacts/maplibre-particle/android-validation}"
|
|
OUT_APK="$OUT_DIR/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk"
|
|
OUT_INFO="$OUT_DIR/build-info.txt"
|
|
OUT_LOG="$OUT_DIR/build.log"
|
|
|
|
MAX_APK_BYTES="${MAPLIBRE_VALIDATION_MAX_APK_BYTES:-104857600}"
|
|
MAX_LIB_BYTES="${MAPLIBRE_VALIDATION_MAX_LIBMAPLIBRE_BYTES:-83886080}"
|
|
|
|
fail() {
|
|
echo "[validation-apk] ERROR: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
resolve_java_home() {
|
|
if [[ -n "${JAVA_HOME:-}" && -x "$JAVA_HOME/bin/java" ]]; then
|
|
return
|
|
fi
|
|
|
|
local candidate
|
|
for candidate in /usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home; do
|
|
if [[ -x "$candidate/bin/java" ]]; then
|
|
export JAVA_HOME="$candidate"
|
|
return
|
|
fi
|
|
done
|
|
|
|
if [[ -x /usr/libexec/java_home ]]; then
|
|
candidate="$(/usr/libexec/java_home -v 17 2>/dev/null || true)"
|
|
if [[ -n "$candidate" && -x "$candidate/bin/java" ]]; then
|
|
export JAVA_HOME="$candidate"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
fail "Java 17 not found. Set JAVA_HOME to a Java 17 JDK."
|
|
}
|
|
|
|
resolve_android_home() {
|
|
local candidate="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
|
|
if [[ -n "$candidate" && -d "$candidate" ]]; then
|
|
export ANDROID_HOME="$candidate"
|
|
export ANDROID_SDK_ROOT="$candidate"
|
|
return
|
|
fi
|
|
|
|
candidate="$HOME/Library/Android/sdk"
|
|
if [[ -d "$candidate" ]]; then
|
|
export ANDROID_HOME="$candidate"
|
|
export ANDROID_SDK_ROOT="$candidate"
|
|
return
|
|
fi
|
|
|
|
fail "Android SDK not found. Set ANDROID_HOME or ANDROID_SDK_ROOT."
|
|
}
|
|
|
|
resolve_java_home
|
|
resolve_android_home
|
|
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$PATH"
|
|
|
|
JAVA_VERSION="$("$JAVA_HOME/bin/java" -version 2>&1 | head -n 1)"
|
|
[[ "$JAVA_VERSION" == *'"17.'* ]] || fail "Java 17 required; got: $JAVA_VERSION"
|
|
[[ -x "$ANDROID_DIR/gradlew" ]] || fail "Gradle wrapper not found: $ANDROID_DIR/gradlew"
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
rm -f "$SOURCE_APK" "$OUT_APK"
|
|
|
|
COMMIT="$(git -C "$REPO_ROOT" rev-parse HEAD)"
|
|
BRANCH="$(git -C "$REPO_ROOT" branch --show-current)"
|
|
DIRTY_COUNT="$(git -C "$REPO_ROOT" status --porcelain | wc -l | tr -d ' ')"
|
|
|
|
echo "[validation-apk] commit=$COMMIT branch=$BRANCH"
|
|
echo "[validation-apk] java=$JAVA_VERSION"
|
|
echo "[validation-apk] android_sdk=$ANDROID_HOME"
|
|
echo "[validation-apk] output=$OUT_APK"
|
|
|
|
set +e
|
|
(
|
|
cd "$ANDROID_DIR"
|
|
./gradlew :MapLibreAndroidTestApp:assembleOpenglDebug -Pmaplibre.abis=x86_64 -Pmaplibre.validation.stripNativeDebugSymbols=true --console=plain
|
|
) >"$OUT_LOG" 2>&1
|
|
GRADLE_RC=$?
|
|
set -e
|
|
|
|
if [[ "$GRADLE_RC" -ne 0 ]]; then
|
|
tail -n 120 "$OUT_LOG" >&2 || true
|
|
fail "Gradle build failed with exit code $GRADLE_RC. Full log: $OUT_LOG"
|
|
fi
|
|
|
|
[[ -f "$SOURCE_APK" ]] || fail "Build succeeded but APK was not produced."
|
|
unzip -tq "$SOURCE_APK" >/dev/null || fail "APK ZIP integrity check failed."
|
|
|
|
ABI_LIST="$(unzip -Z1 "$SOURCE_APK" | sed -n 's#^lib/\([^/]*\)/.*#\1#p' | sort -u | tr '\n' ' ' | sed 's/ $//')"
|
|
# The Android 13.6.1 Test App includes Sentry auxiliary libraries for other ABIs.
|
|
# Only x86_64 may contain MapLibre or CustomLayer native code; do not reject
|
|
# harmless, explicitly whitelisted Sentry files or relax the core ABI contract.
|
|
[[ " $ABI_LIST " == *" x86_64 "* ]] || fail "x86_64 ABI missing: [$ABI_LIST]"
|
|
UNEXPECTED_ABI_LIBS="$(unzip -Z1 "$SOURCE_APK" | grep '^lib/' | grep -v '^lib/x86_64/' | grep -Ev '^lib/(arm64-v8a|armeabi-v7a|x86)/libsentry(-android)?\.so$' || true)"
|
|
[[ -z "$UNEXPECTED_ABI_LIBS" ]] || fail "Non-target ABI contains unexpected libraries: [$UNEXPECTED_ABI_LIBS]"
|
|
[[ -n "$(unzip -Z1 "$SOURCE_APK" | grep '^lib/x86_64/libexample-custom-layer\.so$')" ]] || fail "lib/x86_64/libexample-custom-layer.so missing."
|
|
|
|
|
|
LIB_BYTES="$(unzip -l "$SOURCE_APK" 'lib/x86_64/libmaplibre.so' | awk '/lib\/x86_64\/libmaplibre\.so$/ {print $1}')"
|
|
[[ -n "$LIB_BYTES" ]] || fail "lib/x86_64/libmaplibre.so not found in APK."
|
|
|
|
APK_BYTES="$(wc -c < "$SOURCE_APK" | tr -d ' ')"
|
|
if (( LIB_BYTES > MAX_LIB_BYTES )); then
|
|
fail "libmaplibre.so is too large ($LIB_BYTES bytes). Native debug symbols may not be stripped."
|
|
fi
|
|
if (( APK_BYTES > MAX_APK_BYTES )); then
|
|
fail "APK is too large ($APK_BYTES bytes). Refuse to publish a likely stale/unstripped package."
|
|
fi
|
|
|
|
cp -f "$SOURCE_APK" "$OUT_APK"
|
|
SHA256="$(shasum -a 256 "$OUT_APK" | awk '{print $1}')"
|
|
BUILT_AT_UTC="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
|
GIT_DIRTY="false"
|
|
[[ "$DIRTY_COUNT" -gt 0 ]] && GIT_DIRTY="true"
|
|
|
|
cat >"$OUT_INFO" <<EOF
|
|
pipeline=particle-android-validation-v1
|
|
commit=$COMMIT
|
|
branch=$BRANCH
|
|
git_dirty=$GIT_DIRTY
|
|
built_at_utc=$BUILT_AT_UTC
|
|
variant=OpenglDebug
|
|
abi=x86_64
|
|
bundled_abis=$ABI_LIST
|
|
strip_native_debug_symbols=true
|
|
apk=$OUT_APK
|
|
apk_bytes=$APK_BYTES
|
|
libmaplibre_bytes=$LIB_BYTES
|
|
sha256=$SHA256
|
|
gradle_log=$OUT_LOG
|
|
EOF
|
|
|
|
echo "[validation-apk] BUILD SUCCESS"
|
|
echo "[validation-apk] apk=$OUT_APK"
|
|
echo "[validation-apk] apk_bytes=$APK_BYTES"
|
|
echo "[validation-apk] libmaplibre_bytes=$LIB_BYTES"
|
|
echo "[validation-apk] sha256=$SHA256"
|
|
echo "[validation-apk] info=$OUT_INFO"
|
|
echo "[validation-apk] log=$OUT_LOG"
|