Benchmarks / CPU and I/O (per commit) (push) Successful in 2m52s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Successful in 44m13s
Build and test / Layer separation (push) Successful in 56s
🐳 Android image / Build and push (push) Successful in 17m16s
Build and test / android-image (push) Successful in 17m17s
Traceability / Requirement traces (push) Successful in 1m6s
Build and test / Android (aarch64) (push) Successful in 23m37s
The release keystore now exists and its four secrets are loaded into Gitea, so CI produces an APK a device can update in place. Until now every build, CI and local alike, was signed with a throwaway debug key -- CI's fresh per run, the local one exactly as durable as the cache directory it lived in -- and the night that cache was cleared, no build anywhere could install over the tablet's copy. package.sh forwards KEYSTORE_PASS, KEY_PASS and KEY_ALIAS into the container and copies the keystore under the mounted target directory for the build, so a local release-signed build is one environment line. The doc records where the local copy of the key lives.
87 lines
3.6 KiB
Markdown
87 lines
3.6 KiB
Markdown
# Signing the Android build
|
|
|
|
Every build produces an APK. Which key signs it depends entirely on whether
|
|
four secrets are present:
|
|
|
|
| secret | what it is |
|
|
|---|---|
|
|
| `ANDROID_KEYSTORE_BASE64` | the keystore file, base64-encoded |
|
|
| `ANDROID_KEYSTORE_PASSWORD` | the store password |
|
|
| `ANDROID_KEY_ALIAS` | the alias of the key inside the store |
|
|
| `ANDROID_KEY_PASSWORD` | the key password |
|
|
|
|
With none of them set, `docker/android/assemble-apk.sh` generates a throwaway
|
|
debug key and signs with that. That is the right answer for a branch build or
|
|
a fork: the APK installs on a test device and nothing pretends it is a
|
|
release. With all of them set, the same script signs with the real key.
|
|
|
|
The names match JellyTau's deliberately. One convention across both Android
|
|
projects is one thing to remember instead of two.
|
|
|
|
## Making the key
|
|
|
|
Once, and then never again — keep it forever. Android identifies an app by
|
|
its signature, so an app signed with a new key is a *different* app to every
|
|
device that has the old one installed. There is no recovery from losing it
|
|
beyond telling everybody to uninstall and reinstall.
|
|
|
|
keytool -genkeypair -v \
|
|
-keystore darkroom-release.jks \
|
|
-alias darkroom \
|
|
-keyalg RSA -keysize 4096 -validity 10000 \
|
|
-dname "CN=Duncan Tourolle, O=tourolle.paris, C=FR"
|
|
|
|
`keytool` prompts for the passwords rather than taking them on the command
|
|
line, which keeps them out of shell history. Back the `.jks` up somewhere that
|
|
is not this repository and not the machine that builds it.
|
|
|
|
**The key exists, since 2026-09-11.** It was made as above, with a random
|
|
password, and the four secrets are loaded. The local copy is at
|
|
`~/.config/darkroom/signing/` on the development desktop — `darkroom-release.jks`
|
|
beside `storepass` and `keypass`, all mode 600 in a mode 700 directory. That
|
|
copy is what `package.sh` can sign with locally:
|
|
|
|
D=~/.config/darkroom/signing
|
|
KEYSTORE="$D/darkroom-release.jks" KEYSTORE_PASS="$(cat "$D/storepass")" \
|
|
KEY_ALIAS=darkroom ./docker/android/package.sh --install
|
|
|
|
Before it existed, every build — CI and local alike — was signed with a
|
|
throwaway debug key, and a debug key is exactly as durable as the cache
|
|
directory it lives in: the local one was regenerated the night the cache was
|
|
cleared, at which point no build anywhere could install over the device's copy.
|
|
Any device that received a build from before this date has to uninstall once.
|
|
|
|
## Loading the secrets
|
|
|
|
base64 -w0 darkroom-release.jks > /tmp/ks.b64
|
|
tea api --method PUT /repos/dtourolle/DarkRoom/actions/secrets/ANDROID_KEYSTORE_BASE64 \
|
|
-f data=@/tmp/ks.b64
|
|
shred -u /tmp/ks.b64
|
|
|
|
# and the three strings, read rather than typed so they miss the history
|
|
read -rs PW && tea api --method PUT \
|
|
/repos/dtourolle/DarkRoom/actions/secrets/ANDROID_KEYSTORE_PASSWORD -f data="$PW"
|
|
|
|
...and the same for `ANDROID_KEY_PASSWORD` and `ANDROID_KEY_ALIAS`. Or paste
|
|
them into Settings → Actions → Secrets in the web UI, which is less fiddly and
|
|
just as good.
|
|
|
|
## Checking which key signed a build
|
|
|
|
The packaging step prints it, and the APK carries it:
|
|
|
|
apksigner verify --print-certs darkroom.apk
|
|
|
|
A debug build says `CN=Android Debug`. Anything else is the real key.
|
|
|
|
## Signing locally
|
|
|
|
`docker/android/package.sh` takes the same environment variables, so a local
|
|
release-signed build is:
|
|
|
|
KEYSTORE=$PWD/darkroom-release.jks KEY_ALIAS=darkroom \
|
|
KEYSTORE_PASS=... KEY_PASS=... ./docker/android/package.sh
|
|
|
|
Without them it debug-signs, and keeps one debug keystore in the build cache
|
|
so repeat installs to a device do not need an uninstall first.
|