Let build-apk.sh build either variant: ./build-apk.sh [release|debug]

Dev Updater is gaining build modes declared per component as complete
command lines, so the variant is a positional word and nothing else.
Release stays the default and the only one that signs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-02 05:33:50 -04:00
1 parent 8750ff90cb
commit 68e77c7b37
2 files changed
+31 -11

No files matched your search

+3 -1
View File
@@ -204,7 +204,9 @@ first if a remote spawn ever mangles an argument.
to produce the APK to install on a phone (through Dev Updater), or to produce the APK to install on a phone (through Dev Updater), or
`./run-android.sh` to build, install, and launch on the emulator. `./run-android.sh` to build, install, and launch on the emulator.
**The phone gets the release build**, signed with a key the script **The phone gets the release build**, signed with a key the script
generates once under `~/.config/ai-app/release.jks` (never in the repo). generates once under `~/.config/ai-app/release.jks` (never in the repo);
`./build-apk.sh debug` builds the other variant, and Dev Updater's build
modes call the script with exactly that word.
The emulator scripts stay on the debug build; a debuggable build runs The emulator scripts stay on the debug build; a debuggable build runs
Compose at a fraction of release speed, so never read a frame time from Compose at a fraction of release speed, so never read a frame time from
one as the app's -- the render report now says which build it came from. one as the app's -- the render report now says which build it came from.
+28 -10
View File
@@ -1,7 +1,13 @@
#!/bin/sh #!/bin/sh
# Builds the app's APK, ready to install on a phone through Dev Updater. # Builds the app's APK, ready to install on a phone through Dev Updater.
# #
# ./build-apk.sh # ./build-apk.sh the release build, signed (what the phone runs)
# ./build-apk.sh debug the debug build, for reproducing something the
# emulator scripts would build anyway
#
# Dev Updater's `.dev-updater.ron` at the checkout root spells these out as
# build modes, one command line each; it passes nothing else, so the word
# here is the whole interface.
# #
# The APK pins the CA on *this* machine ($XDG_CONFIG_HOME/ai-app/certs/ca.pem, # The APK pins the CA on *this* machine ($XDG_CONFIG_HOME/ai-app/certs/ca.pem,
# or AI_APP_CA), so build it on the machine that runs the backend: an app # or AI_APP_CA), so build it on the machine that runs the backend: an app
@@ -15,6 +21,16 @@
# whatever is under this project's build directory. # whatever is under this project's build directory.
set -eu set -eu
VARIANT=${1:-release}
case "$VARIANT" in
release) TASK=assembleRelease ;;
debug) TASK=assembleDebug ;;
*)
echo "build-apk.sh: unknown variant '$VARIANT' (release, debug)" >&2
exit 2
;;
esac
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR" cd "$SCRIPT_DIR"
@@ -65,7 +81,7 @@ fi
# uninstalling it first: the signatures differ, and Android refuses to # uninstalling it first: the signatures differ, and Android refuses to
# update across them. # update across them.
KEYSTORE="${AI_APP_KEYSTORE:-${XDG_CONFIG_HOME:-$HOME/.config}/ai-app/release.jks}" KEYSTORE="${AI_APP_KEYSTORE:-${XDG_CONFIG_HOME:-$HOME/.config}/ai-app/release.jks}"
if [ ! -f "$KEYSTORE" ]; then if [ "$VARIANT" = release ] && [ ! -f "$KEYSTORE" ]; then
KEYTOOL="${JAVA_HOME:+$JAVA_HOME/bin/keytool}" KEYTOOL="${JAVA_HOME:+$JAVA_HOME/bin/keytool}"
KEYTOOL="${KEYTOOL:-keytool}" KEYTOOL="${KEYTOOL:-keytool}"
if ! command -v "$KEYTOOL" >/dev/null 2>&1; then if ! command -v "$KEYTOOL" >/dev/null 2>&1; then
@@ -81,10 +97,12 @@ if [ ! -f "$KEYSTORE" ]; then
-keyalg RSA -keysize 2048 -validity 10000 \ -keyalg RSA -keysize 2048 -validity 10000 \
-storepass "$PASSWORD" -keypass "$PASSWORD" -dname "CN=ai-app" >/dev/null 2>&1) -storepass "$PASSWORD" -keypass "$PASSWORD" -dname "CN=ai-app" >/dev/null 2>&1)
fi fi
if [ "$VARIANT" = release ]; then
AI_APP_KEYSTORE="$KEYSTORE" AI_APP_KEYSTORE="$KEYSTORE"
AI_APP_KEYSTORE_PASSWORD=$(cat "$KEYSTORE.password") AI_APP_KEYSTORE_PASSWORD=$(cat "$KEYSTORE.password")
export AI_APP_KEYSTORE AI_APP_KEYSTORE_PASSWORD export AI_APP_KEYSTORE AI_APP_KEYSTORE_PASSWORD
echo "==> Signing with $KEYSTORE" echo "==> Signing with $KEYSTORE"
fi
# Dev Updater draws a real progress bar from "@@progress done/total" lines, # Dev Updater draws a real progress bar from "@@progress done/total" lines,
# and ignores anything that isn't exactly that shape. Gradle can't be asked # and ignores anything that isn't exactly that shape. Gradle can't be asked
@@ -95,17 +113,17 @@ echo "==> Signing with $KEYSTORE"
# the total. The build then prints one "> Task :x" line per task as it # the total. The build then prints one "> Task :x" line per task as it
# goes, so counting those against it is the whole mechanism. # goes, so counting those against it is the whole mechanism.
# #
# Task count is not time -- compileReleaseKotlin and dexBuilder are most of # Task count is not time -- the Kotlin compile and dexBuilder are most of
# the wall clock -- so the bar moves unevenly. It is still counted work # the wall clock -- so the bar moves unevenly. It is still counted work
# rather than a guess at how long last time took. # rather than a guess at how long last time took.
TASKS=$(./gradlew :androidApp:assembleRelease --dry-run --console=plain 2>/dev/null \ TASKS=$(./gradlew :androidApp:$TASK --dry-run --console=plain 2>/dev/null \
| grep -c '^:[A-Za-z:]* SKIPPED' || true) | grep -c '^:[A-Za-z:]* SKIPPED' || true)
echo "==> Building" echo "==> Building"
if [ "${TASKS:-0}" -gt 0 ]; then if [ "${TASKS:-0}" -gt 0 ]; then
echo "@@progress 0/$TASKS" echo "@@progress 0/$TASKS"
DONE=0 DONE=0
./gradlew :androidApp:assembleRelease --console=plain 2>&1 | while IFS= read -r line; do ./gradlew :androidApp:$TASK --console=plain 2>&1 | while IFS= read -r line; do
echo "$line" echo "$line"
case "$line" in case "$line" in
"> Task "*) "> Task "*)
@@ -117,19 +135,19 @@ if [ "${TASKS:-0}" -gt 0 ]; then
# The pipeline's exit status is the shell's, not gradle's, so ask # The pipeline's exit status is the shell's, not gradle's, so ask
# gradle again rather than reporting a failed build as a success. It is # gradle again rather than reporting a failed build as a success. It is
# up to date by now, so this is a second or two. # up to date by now, so this is a second or two.
./gradlew :androidApp:assembleRelease --console=plain >/dev/null ./gradlew :androidApp:$TASK --console=plain >/dev/null
else else
./gradlew :androidApp:assembleRelease ./gradlew :androidApp:$TASK
fi fi
APK="$SCRIPT_DIR/androidApp/build/outputs/apk/release/androidApp-release.apk" APK="$SCRIPT_DIR/androidApp/build/outputs/apk/$VARIANT/androidApp-$VARIANT.apk"
echo echo
echo "==> Built $APK" echo "==> Built $APK"
[ -f "$APK" ] && ls -lh "$APK" | awk '{print " " $5}' [ -f "$APK" ] && ls -lh "$APK" | awk '{print " " $5}'
echo echo
echo "To get it onto the phone: add this project to Dev Updater (or hit" echo "To get it onto the phone: add this project to Dev Updater (or hit"
echo "Update on it if it's already there) and install from there." echo "Update on it if it's already there) and install from there."
echo "If the phone still has the old debug build, uninstall that first:" echo "Debug and release are signed with different keys, so switching from"
echo "it is signed with a different key, so Android will refuse the update." echo "one to the other means uninstalling the installed one first."
echo "Then start the backend and scan the enrollment QR it prints:" echo "Then start the backend and scan the enrollment QR it prints:"
echo " ./server/target/release/ai-server --rotate-token" echo " ./server/target/release/ai-server --rotate-token"