Merge pull request 'Persist the debug signing key across container builds' (#45) from fm/tv-stable-key into main

This commit is contained in:
justinzeus 2026-11-06 19:54:26 +01:00
commit deb24e3c9f
2 changed files with 71 additions and 4 deletions

View file

@ -1,8 +1,8 @@
# Wolfskin TV container builds # Wolfskin TV container builds
Run from a full Git checkout with the upstream `v*` and `p*` tags available. The 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 host needs Docker, GNU Make, Bash, Git, `realpath` (GNU coreutils), and `flock`
Android SDK. (util-linux), not Java or the Android SDK.
```sh ```sh
git fetch origin --tags 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)`). package (`org.justinvisser.wolfskin.tv.opt`) and launcher label (`Wolfskin (Opt)`).
The launcher icon/banner and release-compatible version name are unchanged: The launcher icon/banner and release-compatible version name are unchanged:
the shared upgrade/updater parser rejects extra suffixes such as `-opt`. the shared upgrade/updater parser rejects extra suffixes such as `-opt`.
`make apk` remains the clean, release-key-signed production build; neither its `make apk` remains the clean, release-key-signed production build. Debug and
signing flow nor the existing debug build changes. 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 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 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 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. 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 ## Release credentials
The default escrow is The default escrow is

View file

@ -35,5 +35,36 @@ if [[ $mode == release ]]; then
exec ./gradlew "$@" exec ./gradlew "$@"
' bash "${gradle_arguments[@]}" "$@" ' bash "${gradle_arguments[@]}" "$@"
else 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[@]}" "$@" docker run "${arguments[@]}" "$image" ./gradlew "${gradle_arguments[@]}" "$@"
fi fi