fix(android): force v1+v2+v3 signing & clean-build guards in companion publish
The published companion APK was v2-only (AGP silently ignores enableV1Signing for minSdk>=24) and clean builds broke on stray space-named resource dirs. Harden scripts/publish-companion-apk.sh: clean build, remove/ýreject space-named res dirs, force v1+v2+v3 via zipalign+apksigner, and abort unless all three schemes verify. Wire ship-companion.sh to the shared script. Re-sign the served 0.4.10 APK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d1f9e9ce88
commit
ac59771560
@ -9,6 +9,10 @@
|
|||||||
#
|
#
|
||||||
# ./Android/ship-companion.sh
|
# ./Android/ship-companion.sh
|
||||||
#
|
#
|
||||||
|
# The actual build/sign/verify/stage is done by scripts/publish-companion-apk.sh
|
||||||
|
# (single source of truth, shared with the pre-push hook). It does a CLEAN build,
|
||||||
|
# forces v1+v2+v3 signing, and ABORTS if any signature scheme is missing — so a
|
||||||
|
# broken or v2-only APK can never be shipped.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
@ -17,25 +21,15 @@ cd "$ROOT"
|
|||||||
export JAVA_HOME="${JAVA_HOME:-/opt/homebrew/opt/openjdk@17}"
|
export JAVA_HOME="${JAVA_HOME:-/opt/homebrew/opt/openjdk@17}"
|
||||||
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Library/Android/sdk}"
|
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Library/Android/sdk}"
|
||||||
|
|
||||||
APK="Android/app/build/outputs/apk/debug/app-debug.apk"
|
|
||||||
DEST="neode-ui/public/packages/archipelago-companion.apk"
|
DEST="neode-ui/public/packages/archipelago-companion.apk"
|
||||||
OLD_ZIP="neode-ui/public/packages/archipelago-companion.apk.zip"
|
|
||||||
|
|
||||||
echo "==> Building debug APK"
|
echo "==> Building + signing + verifying companion APK"
|
||||||
( cd Android && ./gradlew :app:assembleDebug --console=plain -q )
|
bash scripts/publish-companion-apk.sh
|
||||||
[ -f "$APK" ] || { echo "ERROR: APK not found at $APK" >&2; exit 1; }
|
|
||||||
|
|
||||||
echo "==> Publishing -> $DEST"
|
[ -f "$DEST" ] || { echo "ERROR: served APK not found at $DEST" >&2; exit 1; }
|
||||||
mkdir -p "$(dirname "$DEST")"
|
|
||||||
cp "$APK" "$DEST"
|
|
||||||
# Drop the legacy zipped artifact so the served download is the raw APK only.
|
|
||||||
if [ -f "$OLD_ZIP" ]; then
|
|
||||||
git rm -q --ignore-unmatch "$OLD_ZIP" 2>/dev/null || rm -f "$OLD_ZIP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git add "$DEST"
|
if git diff --cached --quiet -- "$DEST"; then
|
||||||
if git diff --cached --quiet; then
|
echo "==> Nothing to commit (APK unchanged)"
|
||||||
echo "==> Nothing to commit (working tree + APK unchanged)"
|
|
||||||
else
|
else
|
||||||
git commit -q -m "chore(android): update companion apk download"
|
git commit -q -m "chore(android): update companion apk download"
|
||||||
echo "==> Committed"
|
echo "==> Committed"
|
||||||
|
|||||||
Binary file not shown.
@ -4,6 +4,16 @@
|
|||||||
# can install it straight from the link — no unzip step).
|
# can install it straight from the link — no unzip step).
|
||||||
#
|
#
|
||||||
# Run manually, or automatically via the pre-push hook (.githooks/pre-push).
|
# Run manually, or automatically via the pre-push hook (.githooks/pre-push).
|
||||||
|
#
|
||||||
|
# Hardened (2026-06-26) so a broken APK can never ship again:
|
||||||
|
# 1. Aborts on stray resource dirs whose names contain spaces (these break a
|
||||||
|
# clean build with "Invalid resource directory name"). Empty ones — junk
|
||||||
|
# left by some icon-export tools — are auto-removed; non-empty ones error.
|
||||||
|
# 2. Always a CLEAN build (incremental builds masked the bad resource dirs).
|
||||||
|
# 3. Forces v1 + v2 + v3 signing with zipalign + apksigner. AGP's
|
||||||
|
# `enableV1Signing = true` flag is silently ignored for minSdk>=24, which
|
||||||
|
# shipped a v2-only APK that some OEM installers reject ("App not installed").
|
||||||
|
# 4. VERIFIES all three schemes and ABORTS if any is missing — no silent ship.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(git rev-parse --show-toplevel)"
|
ROOT="$(git rev-parse --show-toplevel)"
|
||||||
@ -17,16 +27,63 @@ if [ ! -x "$JAVA/bin/java" ] || [ ! -d "$SDK" ]; then
|
|||||||
echo " (set JAVA_HOME and ANDROID_HOME to build the companion APK)" >&2
|
echo " (set JAVA_HOME and ANDROID_HOME to build the companion APK)" >&2
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
export JAVA_HOME="$JAVA"
|
||||||
|
export PATH="$JAVA/bin:$PATH"
|
||||||
|
|
||||||
echo "publish-companion-apk: building debug APK…" >&2
|
RES="Android/app/src/main/res"
|
||||||
( cd Android && JAVA_HOME="$JAVA" ANDROID_HOME="$SDK" ./gradlew -q :app:assembleDebug )
|
|
||||||
|
|
||||||
APK="Android/app/build/outputs/apk/debug/app-debug.apk"
|
APK="Android/app/build/outputs/apk/debug/app-debug.apk"
|
||||||
|
SIGNED="Android/app/build/outputs/apk/debug/app-debug-signed.apk"
|
||||||
DEST="neode-ui/public/packages/archipelago-companion.apk"
|
DEST="neode-ui/public/packages/archipelago-companion.apk"
|
||||||
OLD_ZIP="neode-ui/public/packages/archipelago-companion.apk.zip"
|
OLD_ZIP="neode-ui/public/packages/archipelago-companion.apk.zip"
|
||||||
mkdir -p "$(dirname "$DEST")"
|
KS="Android/app/debug.keystore"
|
||||||
|
|
||||||
cp "$APK" "$DEST"
|
# 1. Guard against resource dirs with spaces (Android forbids them; a clean
|
||||||
|
# build aborts on them). Empty ones are removed; non-empty ones are fatal.
|
||||||
|
while IFS= read -r d; do
|
||||||
|
[ -n "$d" ] || continue
|
||||||
|
if [ -n "$(ls -A "$d" 2>/dev/null)" ]; then
|
||||||
|
echo "publish-companion-apk: ERROR — resource dir with a space is not empty:" >&2
|
||||||
|
echo " $d" >&2
|
||||||
|
echo " Rename it (Android resource dir names cannot contain spaces)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rmdir "$d" && echo "publish-companion-apk: removed stray empty resource dir: $d" >&2
|
||||||
|
done < <(find "$RES" -type d -name '* *' 2>/dev/null)
|
||||||
|
|
||||||
|
# 2. Clean build.
|
||||||
|
echo "publish-companion-apk: clean build of debug APK…" >&2
|
||||||
|
( cd Android && ./gradlew -q --console=plain :app:clean :app:assembleDebug )
|
||||||
|
[ -f "$APK" ] || { echo "publish-companion-apk: ERROR — APK not produced at $APK" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 3. Force v1 + v2 + v3 signing (AGP's enableV1Signing flag is ignored here).
|
||||||
|
BT="$(ls -d "$SDK"/build-tools/*/ | sort -V | tail -1)"
|
||||||
|
ZIPALIGN="${BT}zipalign"; APKSIGNER="${BT}apksigner"
|
||||||
|
[ -x "$ZIPALIGN" ] && [ -x "$APKSIGNER" ] || {
|
||||||
|
echo "publish-companion-apk: ERROR — zipalign/apksigner not found under $BT" >&2; exit 1; }
|
||||||
|
[ -f "$KS" ] || { echo "publish-companion-apk: ERROR — keystore missing at $KS" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "publish-companion-apk: zipalign + sign (v1+v2+v3)…" >&2
|
||||||
|
"$ZIPALIGN" -p -f 4 "$APK" "$SIGNED"
|
||||||
|
"$APKSIGNER" sign \
|
||||||
|
--ks "$KS" --ks-pass pass:android \
|
||||||
|
--ks-key-alias androiddebugkey --key-pass pass:android \
|
||||||
|
--v1-signing-enabled true --v2-signing-enabled true --v3-signing-enabled true \
|
||||||
|
"$SIGNED"
|
||||||
|
|
||||||
|
# 4. Verify all three schemes (min-sdk 21 forces the v1 path to be exercised).
|
||||||
|
VERIFY="$("$APKSIGNER" verify -v --min-sdk-version 21 "$SIGNED" 2>&1)"
|
||||||
|
for scheme in "v1 scheme" "v2 scheme" "v3 scheme"; do
|
||||||
|
if ! printf '%s\n' "$VERIFY" | grep -iq "$scheme.*: true"; then
|
||||||
|
echo "publish-companion-apk: ERROR — $scheme NOT present after signing. Aborting." >&2
|
||||||
|
printf '%s\n' "$VERIFY" | grep -iE "scheme" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "publish-companion-apk: verified v1 + v2 + v3 signatures." >&2
|
||||||
|
|
||||||
|
# 5. Publish.
|
||||||
|
mkdir -p "$(dirname "$DEST")"
|
||||||
|
cp "$SIGNED" "$DEST"
|
||||||
|
|
||||||
# Drop the legacy zipped artifact so the served download is the raw APK only.
|
# Drop the legacy zipped artifact so the served download is the raw APK only.
|
||||||
if [ -f "$OLD_ZIP" ]; then
|
if [ -f "$OLD_ZIP" ]; then
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user