Files
DarkRoom/docker/android/package.sh
T
dtourolleandClaude Opus 5 5741ec5e00 Lift the APK assembly out of package.sh so CI can run it too
package.sh does two things: it decides how the host reaches the image, and
it assembles an APK once inside it. Only the first half is host-specific.
CI already runs in that image, so the second half was about to be copied
into a workflow step -- two copies of aapt2/zipalign/apksigner ordering,
drifting apart at whatever rate the toolchain moves.

So it moves to docker/android/assemble-apk.sh, which assumes it is inside
the image and takes its paths from the environment, because the callers
disagree about them: the container mounts the repo at /work, the runner
checks it out wherever it likes. Every default reproduces what package.sh
did, so the host path is unchanged.

Two things stop being hard-coded on the way. The build-tools version and
the compile SDK are resolved from what is installed rather than written
out as 36.0.0 and android-36 -- the versions are Dockerfile ARGs, and a
second copy is a second thing to miss when they move. --min-sdk-version
now comes from that same ARG instead of a literal 28, which is the number
the API-level check in CI already reads.

The intermediates are removed at the end. They were harmless in a cache
directory nobody looks at; beside a published artefact they are four more
files for a glob to pick up by mistake.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 23:38:08 +02:00

89 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build DarkRoom into an installable APK, without Gradle.
#
# ./docker/android/package.sh # build + package, debug-signed
# ./docker/android/package.sh --install # ...and adb install to a device
#
# Gradle would add a second build system, a second dependency tree, and a
# second place for the toolchain versions to drift out of step with the
# Dockerfile. The four tools it would have driven — aapt2, d8, zipalign,
# apksigner — are in build-tools already and are enough on their own, because
# the app has no Java of its own: android-activity's glue calls android_main
# directly, and the only classes in the APK are the ones Slint's build script
# compiles for its own helper.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "${HERE}/../.." && pwd)"
ABI="arm64-v8a"
RUST_TARGET="aarch64-linux-android"
PKG_DIR="${REPO}/apps/darkroom-android/android"
# The container writes here (see build.sh); the APK is assembled in the same
# place so both halves of the build agree on one output directory.
CACHE="${XDG_CACHE_HOME:-${HOME}/.cache}/darkroom-android"
# Under the cache's target/ rather than beside it: the container writes to
# /work/target-android/apk, and that is the directory bind-mounted here.
OUT="${CACHE}/target/apk"
APK="${OUT}/darkroom.apk"
# The version, taken from the workspace rather than restated here.
#
# `AndroidManifest.xml` deliberately declares none: a manifest that states a
# version is a second place for one to be wrong, and it was — the APK reported
# `versionName=null` and `versionCode=0` on the device while the binary inside
# it knew perfectly well what it was.
#
# `versionCode` must be a single increasing integer, which a semantic version
# is not, so it is packed: MAJOR*10000 + MINOR*100 + PATCH. That keeps the
# ordering Android needs (it refuses to install an APK whose code is lower than
# the installed one) and stays readable — 0.4.0 is 400. It allows 99 minors and
# 99 patches per major, which is a limit worth knowing about and a long way off.
VERSION_NAME="$(sed -n 's/^version = "\(.*\)"$/\1/p' "${REPO}/Cargo.toml" | head -1)"
if [[ -z "${VERSION_NAME}" ]]; then
echo "error: no version in Cargo.toml" >&2
exit 1
fi
VERSION_CODE="$(awk -F. '{ print $1 * 10000 + $2 * 100 + $3 }' <<< "${VERSION_NAME}")"
echo "==> version ${VERSION_NAME} (code ${VERSION_CODE})"
INSTALL=0
[[ "${1:-}" == "--install" ]] && INSTALL=1
# ---------------------------------------------------------------------------
# 1. Cross-compile the shared library.
# ---------------------------------------------------------------------------
echo "==> building libdarkroom.so (${ABI})"
"${HERE}/build.sh" cargo ndk -t "${ABI}" -o /work/target-android/jniLibs \
build --release -p darkroom-android
SO="${CACHE}/target/jniLibs/${ABI}/libdarkroom.so"
[[ -f "${SO}" ]] || { echo "error: ${SO} not built" >&2; exit 1; }
# ---------------------------------------------------------------------------
# 2. Assemble the APK inside the container, where the SDK lives.
#
# The assembly itself is assemble-apk.sh, which runs in the image and is shared
# with CI — see its header. Only the mount layout is decided here: the repo is
# at /work and the cache's target directory at /work/target-android, so every
# default in that script already points at the right place.
# ---------------------------------------------------------------------------
echo "==> packaging APK"
"${HERE}/build.sh" env \
ABI="${ABI}" RUST_TARGET="${RUST_TARGET}" \
/work/docker/android/assemble-apk.sh
# ---------------------------------------------------------------------------
# 3. Install from the host.
#
# The container has adb but no device: build.sh mounts no USB and shares no
# network, so the host's already-authorised adb server is the shorter path.
# ---------------------------------------------------------------------------
if [[ "${INSTALL}" == "1" ]]; then
command -v adb >/dev/null || { echo "error: adb not on PATH" >&2; exit 1; }
echo "==> installing"
adb install -r "${APK}"
echo "==> launching"
adb shell am start -n paris.tourolle.darkroom/android.app.NativeActivity
fi