kepr-build-apk.sh

5.3 KB · raw

1#!/bin/sh2# Build and publish the release APK for Kepr Builds.3#4# Kepr Beta builds use the established release certificate. Validate the produced5# APK's package, version, ABI, and signing identity before staging it.6set -eu78# PB-26-008: tool paths are discovered from the environment first, then a9# Homebrew default, then fail closed. No silent fallback to an unknown path.10JAVA_HOME="${PEACH_JAVA_HOME:-/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home}"11if [ ! -d "$JAVA_HOME" ]; then12    echo "refusing to publish: Java 17 runtime not found at $JAVA_HOME (set PEACH_JAVA_HOME)" >&213    exit 114fi15export JAVA_HOME1617ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)18cd "$ROOT_DIR"1920# Keep the tool-selection and public metadata gates shared with the hermetic21# negative-path test; this script owns build orchestration only.22# shellcheck source=scripts/kepr-build-helpers.sh23. "$ROOT_DIR/scripts/kepr-build-helpers.sh"2425# Resolve the SDK from the environment first, then the non-secret sdk.dir in26# local.properties. Pick the newest *versioned* build-tools directory containing both27# tools; build-tools/latest is intentionally never trusted or consulted.28SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"29if [ -z "$SDK_ROOT" ]; then30    SDK_ROOT=$(sed -n 's/^sdk\.dir=//p' "$ROOT_DIR/local.properties" 2>/dev/null | sed -n '1p')31fi32if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT/build-tools" ]; then33    echo "refusing to publish: Android SDK build-tools directory not found" >&234    exit 135fi3637BUILD_TOOLS_DIR=$(find_stable_build_tools_dir "$SDK_ROOT" || true)38if [ -z "$BUILD_TOOLS_DIR" ]; then39    echo "refusing to publish: no versioned build-tools directory has aapt2" >&240    exit 141fi42AAPT2="$BUILD_TOOLS_DIR/aapt2"4344ROOT_VERSION=$(read_root_semver "$ROOT_DIR/VERSION" || true)45if [ -z "$ROOT_VERSION" ]; then46    echo "refusing to publish: VERSION is missing or not strict semantic version" >&247    exit 148fi49# PB-26-009: Gradle reads release signing passwords during configuration, so50# source the existing protected environment before invoking any release task.51if [ -f "$HOME/.secrets" ]; then52    . "$HOME/.secrets"53fi5455"$ROOT_DIR/gradlew" --no-configuration-cache :app:assembleRelease5657# PB-26-113: the merged release manifest is the shipped permission and exported58# component surface. Drift from the checked-in inventory refuses the publish59# rather than reaching a store unnoticed.60python3 "$ROOT_DIR/scripts/check-release-manifest.py" check ||61    {62        echo "refusing to publish: release manifest inventory drift" >&263        exit 164    }6566case "${KEPR_TARGET:-}" in67    android-arm64)68        APK_NAME="app-arm64-v8a-release.apk"69        EXPECTED_ABI="arm64-v8a"70        ;;71    android-x86)72        APK_NAME="app-x86_64-release.apk"73        EXPECTED_ABI="x86_64"74        ;;75    *)76        echo "refusing to publish: unsupported KEPR_TARGET '${KEPR_TARGET:-unset}'" >&277        exit 178        ;;79esac8081APK="$ROOT_DIR/app/build/outputs/apk/release/$APK_NAME"82METADATA="$ROOT_DIR/app/build/outputs/apk/release/output-metadata.json"83if [ ! -f "$APK" ] || [ ! -r "$APK" ]; then84    echo "refusing to publish: release APK was not produced" >&285    exit 186fi87if [ ! -f "$METADATA" ] || [ ! -r "$METADATA" ]; then88    echo "refusing to publish: Gradle output metadata was not produced" >&289    exit 190fi9192# PB-26-123: the inventory is an allow-list of native libraries. It is checked93# against the source set AND against the packaged APK, so a library that94# arrived only through a dependency is refused too.95python3 "$ROOT_DIR/scripts/check-native-provenance.py" "$APK" ||96    {97        echo "refusing to publish: native provenance drift" >&298        exit 199    }100101BADGING_FILE=$(mktemp "${TMPDIR:-/tmp}/peach-apk-badging.XXXXXX")102cleanup_badging() {103    [ -e "$BADGING_FILE" ] && trash "$BADGING_FILE"104}105trap cleanup_badging EXIT HUP INT TERM106"$AAPT2" dump badging "$APK" > "$BADGING_FILE"107if ! validate_release_metadata \108    "$METADATA" \109    "$BADGING_FILE" \110    "$ROOT_VERSION" \111    "com.peachpasswords" \112    "$APK_NAME" \113    "$EXPECTED_ABI"; then114    exit 1115fi116117RELEASE_KEYSTORE=$(sed -n 's/^peachStoreFile=//p' "$ROOT_DIR/local.properties" | sed -n '1p')118RELEASE_ALIAS=$(sed -n 's/^peachKeyAlias=//p' "$ROOT_DIR/local.properties" | sed -n '1p')119RELEASE_STOREPASS="${PEACH_STORE_PASSWORD:-}"120if [ -z "$RELEASE_STOREPASS" ]; then121    echo "refusing to publish: PEACH_STORE_PASSWORD is not set (source ~/.secrets)" >&2122    exit 1123fi124if [ -z "$RELEASE_KEYSTORE" ] || [ -z "$RELEASE_ALIAS" ]; then125    echo "refusing to publish: peachStoreFile or peachKeyAlias missing from local.properties" >&2126    exit 1127fi128RELEASE_CERTIFICATE=$(release_certificate_sha256 "$RELEASE_KEYSTORE" "$RELEASE_STOREPASS" "$RELEASE_ALIAS")129if ! assert_established_release_certificate "$RELEASE_CERTIFICATE"; then130    exit 1131fi132if ! assert_release_signer_matches \133    "$RELEASE_CERTIFICATE" \134    "$(apk_signer_sha256 "$BUILD_TOOLS_DIR/apksigner" "$APK")" \135    "$APK_NAME"; then136    exit 1137fi138139# Only a fully verified artifact reaches the Kepr staging directory. The140# basename includes SemVer + ABI so Kepr's Content-Disposition carries both141# to the browser, matching the {version}/{platform} tokens in kepr.build.142mkdir -p "$ROOT_DIR/build/kepr"143STAGED_NAME="peach-android-${ROOT_VERSION}-${KEPR_TARGET}.apk"144cp "$APK" "$ROOT_DIR/build/kepr/$STAGED_NAME"145echo "release APK verified and staged: build/kepr/$STAGED_NAME"