diff --git a/docker/README.md b/docker/README.md index e2e760c6..228da088 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,8 +1,8 @@ # Wolfskin TV container builds Run from a full Git checkout with the upstream `v*` and `p*` tags available. The -host needs Docker, GNU Make, Bash, Git, and `flock` (util-linux), not Java or the -Android SDK. +host needs Docker, GNU Make, Bash, Git, `realpath` (GNU coreutils), and `flock` +(util-linux), not Java or the Android SDK. ```sh git fetch origin --tags @@ -35,8 +35,9 @@ dependency baseline profiles. It changes only signing (the Android debug key), package (`org.justinvisser.wolfskin.tv.opt`) and launcher label (`Wolfskin (Opt)`). The launcher icon/banner and release-compatible version name are unchanged: the shared upgrade/updater parser rejects extra suffixes such as `-opt`. -`make apk` remains the clean, release-key-signed production build; neither its -signing flow nor the existing debug build changes. +`make apk` remains the clean, release-key-signed production build. Debug and +optimized containers share the persistent debug key described below; release +signing is unchanged. Build all four default-flavor outputs in one invocation through the container wrapper, without a release keystore. On a host whose Gradle cache already holds @@ -814,6 +815,41 @@ whose parent directory already exists. All callers sharing a volume must use the same lock path on the Docker host. Do not delete the file while builds are running or waiting. The lock releases automatically when the invocation exits. +## Persistent debug signing + +`make apk-debug` and non-release `docker/gradle.sh` invocations (including +`:app:assembleDefaultOptimized`) use one host keystore, outside the checkout: + +```text +/root/.treehouse/firstmate-7bab20/2/firstmate/data/signing/debug.keystore +``` + +Set `WOLFSKIN_DEBUG_KEYSTORE` to another file in a dedicated directory outside +the checkout when building on another host, for example: + +```sh +WOLFSKIN_DEBUG_KEYSTORE="$HOME/.local/share/wolfskin-signing/debug.keystore" make apk-debug +``` + +The wrapper creates a missing key once using the build image's `keytool`: RSA +2048, 10,000-day validity, alias `androiddebugkey`, store/key passwords `android`, +and subject `CN=Android Debug`. The containing directory is mode `0700`, the +file mode `0600`. Existing keys are reused, never regenerated; generation errors +abort the build rather than falling back to an ephemeral container key. +The file is bind-mounted read-only at `/tmp/.android/debug.keystore`, with +`ANDROID_USER_HOME=/tmp/.android`, for every non-release Gradle container. +Keep and back up this file across clean builds, disposable worktrees, container +rebuilds and Gradle cache resets. Never commit it. The standard debug passwords +are public; this key is for local debug/optimized builds, not release signing. + +**One-time reinstall:** the first build with the stable key still needs one +uninstall+reinstall on the box because the signer changes once; later ones +install in place with `adb install -r`, retaining sign-ins and settings. +The initial uninstall removes app data, so sign in again once after that switch. +Deleting/replacing the persistent key or choosing a different override changes +the signer again and requires another reinstall. + + ## Release credentials The default escrow is diff --git a/docker/gradle.sh b/docker/gradle.sh index b5624cbf..461018bc 100755 --- a/docker/gradle.sh +++ b/docker/gradle.sh @@ -35,5 +35,36 @@ if [[ $mode == release ]]; then exec ./gradlew "$@" ' bash "${gradle_arguments[@]}" "$@" else + debug_keystore=$(realpath -m "${WOLFSKIN_DEBUG_KEYSTORE:-/root/.treehouse/firstmate-7bab20/2/firstmate/data/signing/debug.keystore}") + case "$debug_keystore" in + "$root"|"$root"/*) + printf 'Debug keystore must be outside the checkout: %s\n' "$debug_keystore" >&2 + exit 1 + ;; + esac + signing_directory=$(dirname "$debug_keystore") + mkdir -p "$signing_directory" + chmod 700 "$signing_directory" + if [[ ! -e $debug_keystore ]]; then + temporary_directory=$(mktemp -d "$signing_directory/.debug-keystore.XXXXXX") + trap 'rm -rf -- "$temporary_directory"' EXIT + if ! docker run --rm --platform linux/amd64 --user "$(id -u):$(id -g)" \ + --mount "type=bind,src=$temporary_directory,dst=/run/signing" \ + "$image" keytool -genkeypair -noprompt -keystore /run/signing/debug.keystore \ + -storetype JKS -storepass android -keypass android -alias androiddebugkey \ + -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug"; then + printf 'Failed to generate persistent debug keystore: %s\n' "$debug_keystore" >&2 + exit 1 + fi + chmod 600 "$temporary_directory/debug.keystore" + mv "$temporary_directory/debug.keystore" "$debug_keystore" + fi + if [[ ! -s $debug_keystore ]]; then + printf 'Missing or empty persistent debug keystore: %s\n' "$debug_keystore" >&2 + exit 1 + fi + chmod 600 "$debug_keystore" + arguments+=(--env ANDROID_USER_HOME=/tmp/.android + --mount "type=bind,src=$debug_keystore,dst=/tmp/.android/debug.keystore,readonly") docker run "${arguments[@]}" "$image" ./gradlew "${gradle_arguments[@]}" "$@" fi