mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
edcb502a2c
`build:android-binaries` compiles the helper from the `packages/argent-private` submodule and stamps `--version-code` from this repo's manifest. Nothing tied those two together, so bumping the manifest to 2 for the `setText` protocol — without moving the submodule pointer, which is still v0.19.0 and `PROTOCOL_VERSION = "1"` — mints an APK that claims versionCode 2 and cannot answer the method. That fails silently and permanently. `ensureAndroidDevtoolsInstalled` compares `probe.versionCode >= manifest.versionCode`, `2 >= 2` is satisfied, and the real v2 helper is never pushed. Reproduced on an API 36 emulator: with such an APK installed and a GENUINE protocol-2 APK bundled, the install is skipped, the device still reports versionCode 2, and every clear comes back `helper_outdated`. `adb uninstall` is the only recovery. The manifest now states the protocol it promises, and `build.sh` compares it against the source's own `PROTOCOL_VERSION` before compiling anything. Kept as its own field rather than read off the versionCode: the versionCode is the install bar and a rebuild for a plain bug fix moves it alone. Verified both ways on the pinned tree: the build now stops with the pointer's protocol named and `bin/` untouched, and the same extraction against the protocol-2 helper source answers 2, which is what the manifest declares.
180 lines
7.0 KiB
Bash
Executable File
180 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build native-devtools-android APK for Argent.
|
|
# Java sources live in the argent-private submodule at
|
|
# packages/argent-private/packages/native-devtools-android/Sources/AndroidDevtools/
|
|
# Mirrors `packages/native-devtools-ios/scripts/build.sh` shape.
|
|
#
|
|
# Run from the workspace root: bash packages/native-devtools-android/scripts/build.sh
|
|
# Or from this package: bash scripts/build.sh
|
|
#
|
|
# `versionName` and `versionCode` are sourced from manifest.json — bump that
|
|
# file in git when releasing a new helper, then rebuild.
|
|
#
|
|
# Environment:
|
|
# PREBUILT_ANDROID_DEVTOOLS_APK if set, copy this path to bin/ instead of
|
|
# building (used by CI / non-Android-SDK hosts)
|
|
#
|
|
# Required tools (resolved via $ANDROID_HOME):
|
|
# - javac (system)
|
|
# - $ANDROID_HOME/build-tools/$BUILD_TOOLS/{d8,aapt2,zipalign,apksigner}
|
|
# - $ANDROID_HOME/platforms/android-$COMPILE_SDK/android.jar
|
|
set -euo pipefail
|
|
|
|
PKG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
WORKSPACE_DIR="$(cd "${PKG_DIR}/../.." && pwd)"
|
|
SUBMODULE_DIR="${WORKSPACE_DIR}/packages/argent-private/packages/native-devtools-android"
|
|
SRC_DIR="${SUBMODULE_DIR}/Sources/AndroidDevtools"
|
|
BIN_DIR="${PKG_DIR}/bin"
|
|
BUILD_DIR="${PKG_DIR}/.build"
|
|
|
|
VERSION="$(node -p "require('${PKG_DIR}/assets/manifest.json').versionName")"
|
|
VERSION_CODE="$(node -p "require('${PKG_DIR}/assets/manifest.json').versionCode")"
|
|
|
|
APK_OUT="${BIN_DIR}/argent-android-devtools-${VERSION}.apk"
|
|
mkdir -p "${BIN_DIR}"
|
|
|
|
# If a prebuilt APK is provided (CI on a non-Android host, or local PREBUILT
|
|
# override), copy it and skip the build pipeline.
|
|
if [[ -n "${PREBUILT_ANDROID_DEVTOOLS_APK:-}" ]]; then
|
|
echo "Using prebuilt APK from PREBUILT_ANDROID_DEVTOOLS_APK"
|
|
cp "${PREBUILT_ANDROID_DEVTOOLS_APK}" "${APK_OUT}"
|
|
echo "Done. APK at ${APK_OUT}"
|
|
exit 0
|
|
fi
|
|
|
|
# Verify the submodule is initialised before trying to build.
|
|
if [[ ! -d "${SRC_DIR}" ]]; then
|
|
echo "Error: Android source not found at ${SRC_DIR}" >&2
|
|
echo " Run: git submodule update --init packages/argent-private" >&2
|
|
echo " Or set PREBUILT_ANDROID_DEVTOOLS_APK=/path/to/argent-android-devtools.apk" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The APK is COMPILED from the submodule and STAMPED from manifest.json, and
|
|
# nothing else ties those two together. A manifest bumped for a helper change the
|
|
# submodule pointer did not follow mints an APK that claims the new versionCode
|
|
# and runs the old code — and that fails SILENTLY once it is installed:
|
|
# `ensureAndroidDevtoolsInstalled` compares device >= manifest, is satisfied, and
|
|
# never replaces it, so every protocol-gated call falls back to its weaker path
|
|
# for the life of that install with `adb uninstall` as the only recovery.
|
|
#
|
|
# `protocolVersion` is what this repo promises the helper answers with;
|
|
# `PROTOCOL_VERSION` in the source is what it really answers with. Disagreement
|
|
# in either direction is a release-coordination bug: one way ships a helper too
|
|
# old for the gates the tool-server asserts, the other leaves the manifest
|
|
# describing a helper that no longer exists.
|
|
MANIFEST_PROTOCOL="$(node -p "require('${PKG_DIR}/assets/manifest.json').protocolVersion")"
|
|
SRC_PROTOCOL="$(sed -n 's/.*PROTOCOL_VERSION = "\([0-9][0-9]*\)".*/\1/p' \
|
|
"${SRC_DIR}/SnapshotInstrumentation.java" | head -1)"
|
|
if [[ "${SRC_PROTOCOL}" != "${MANIFEST_PROTOCOL}" ]]; then
|
|
echo "Error: the pinned helper source answers protocol '${SRC_PROTOCOL:-<unreadable>}', but" >&2
|
|
echo " ${PKG_DIR}/assets/manifest.json declares protocolVersion ${MANIFEST_PROTOCOL}." >&2
|
|
echo " Building anyway would stamp versionCode ${VERSION_CODE} onto that source, and an" >&2
|
|
echo " installed APK whose versionCode satisfies the install gate is never replaced." >&2
|
|
echo " Move the packages/argent-private pointer to the helper commit that carries" >&2
|
|
echo " protocol ${MANIFEST_PROTOCOL} and commit it, or lower protocolVersion (and" >&2
|
|
echo " versionCode with it) to match the pinned source." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${ANDROID_HOME:-}" ]]; then
|
|
if [[ -d "${HOME}/Library/Android/sdk" ]]; then
|
|
export ANDROID_HOME="${HOME}/Library/Android/sdk"
|
|
elif [[ -d "${HOME}/Android/Sdk" ]]; then
|
|
export ANDROID_HOME="${HOME}/Android/Sdk"
|
|
else
|
|
echo "ANDROID_HOME is not set and no default SDK location found." >&2
|
|
echo "Set ANDROID_HOME or PREBUILT_ANDROID_DEVTOOLS_APK." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Pick the highest-numbered build-tools directory that has d8 + aapt2 + zipalign + apksigner.
|
|
BUILD_TOOLS_DIR=""
|
|
for candidate in $(ls -1 "${ANDROID_HOME}/build-tools" 2>/dev/null | sort -V -r); do
|
|
bt="${ANDROID_HOME}/build-tools/${candidate}"
|
|
if [[ -x "${bt}/d8" && -x "${bt}/aapt2" && -x "${bt}/zipalign" && -x "${bt}/apksigner" ]]; then
|
|
BUILD_TOOLS_DIR="${bt}"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z "${BUILD_TOOLS_DIR}" ]]; then
|
|
echo "Could not locate a build-tools directory under ${ANDROID_HOME}/build-tools that contains d8 + aapt2 + zipalign + apksigner." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Pick the highest android-XX platform that ships android.jar.
|
|
ANDROID_JAR=""
|
|
for candidate in $(ls -1 "${ANDROID_HOME}/platforms" 2>/dev/null | sed 's/android-//' | sort -nr); do
|
|
jar="${ANDROID_HOME}/platforms/android-${candidate}/android.jar"
|
|
if [[ -f "${jar}" ]]; then
|
|
ANDROID_JAR="${jar}"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z "${ANDROID_JAR}" ]]; then
|
|
echo "Could not locate android.jar under ${ANDROID_HOME}/platforms/android-*/." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Keystore lives in the submodule. Rotating it requires a major version bump
|
|
# because `adb install -r` enforces signature match across upgrades.
|
|
KEYSTORE="${SRC_DIR}/debug.keystore"
|
|
if [[ ! -f "${KEYSTORE}" ]]; then
|
|
echo "Error: keystore missing from submodule at ${KEYSTORE}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "→ ANDROID_HOME=${ANDROID_HOME}"
|
|
echo "→ build-tools=${BUILD_TOOLS_DIR}"
|
|
echo "→ android.jar=${ANDROID_JAR}"
|
|
echo "→ submodule src=${SRC_DIR}"
|
|
echo "→ version=${VERSION} (code ${VERSION_CODE})"
|
|
|
|
rm -rf "${BUILD_DIR}"
|
|
mkdir -p "${BUILD_DIR}/classes" "${BUILD_DIR}/dex"
|
|
|
|
echo "→ javac"
|
|
JAVA_SOURCES=$(find "${SRC_DIR}" -name '*.java')
|
|
javac --release 11 \
|
|
-classpath "${ANDROID_JAR}" \
|
|
-d "${BUILD_DIR}/classes" \
|
|
${JAVA_SOURCES}
|
|
|
|
echo "→ d8"
|
|
"${BUILD_TOOLS_DIR}/d8" \
|
|
--min-api 23 \
|
|
--classpath "${ANDROID_JAR}" \
|
|
--output "${BUILD_DIR}/dex" \
|
|
$(find "${BUILD_DIR}/classes" -name '*.class')
|
|
|
|
echo "→ aapt2 link"
|
|
"${BUILD_TOOLS_DIR}/aapt2" link \
|
|
--manifest "${SRC_DIR}/AndroidManifest.xml" \
|
|
-I "${ANDROID_JAR}" \
|
|
--min-sdk-version 23 \
|
|
--target-sdk-version 36 \
|
|
--version-code "${VERSION_CODE}" \
|
|
--version-name "${VERSION}" \
|
|
-o "${BUILD_DIR}/unsigned.apk"
|
|
|
|
echo "→ zip classes.dex into APK"
|
|
(cd "${BUILD_DIR}/dex" && zip -j -q "${BUILD_DIR}/unsigned.apk" classes.dex)
|
|
|
|
echo "→ zipalign"
|
|
"${BUILD_TOOLS_DIR}/zipalign" -f 4 "${BUILD_DIR}/unsigned.apk" "${BUILD_DIR}/aligned.apk"
|
|
|
|
echo "→ apksigner"
|
|
"${BUILD_TOOLS_DIR}/apksigner" sign \
|
|
--ks "${KEYSTORE}" \
|
|
--ks-pass pass:android \
|
|
--key-pass pass:android \
|
|
--out "${APK_OUT}" \
|
|
"${BUILD_DIR}/aligned.apk"
|
|
|
|
echo "→ wrote ${APK_OUT} ($(stat -f%z "${APK_OUT}" 2>/dev/null || stat -c%s "${APK_OUT}") bytes)"
|
|
|
|
rm -rf "${BUILD_DIR}"
|