Building
July 30, 2026 · View on GitHub
Toolchain
| JDK | 17 or later |
| Android SDK | compileSdk 36, minSdk 24 |
| Gradle | 8.14.3, via the committed wrapper |
| AGP | 8.13.2 |
| Kotlin | 2.2.20 |
No local Android SDK is needed to get an APK — CI builds one on every push and attaches it to tagged releases.
./gradlew assembleRelease # app/build/outputs/apk/release/app-release.apk
./gradlew assembleDebug # needed for Desktop Head Unit work
./gradlew installDebug # to an attached device
Minification is off, deliberately
isMinifyEnabled = false on the release build. R8 would take the APK from about 9 MB to
roughly 3 MB, and there is nothing in the app that obviously resists it — the one keep
rule that is needed, for the CarAppService the host resolves by name, is already in
proguard-rules.pro.
It is off because nobody has run a minified build on a real device or head unit yet, and
an R8 problem shows up at runtime rather than at build time. Turn it on once someone
has done that, and check both surfaces, particularly the host validator path that reads
androidx.car.app.R.array.hosts_allowlist_sample. Leave shrinkResources off until that
array is confirmed to survive.
Signing
release is deliberately wired to the debug signing config in
app/build.gradle.kts, so a fresh clone and a CI run both
produce an APK that installs without any secret being present. The trade-off is that the
artefact cannot be uploaded to the Play Store and cannot be used to update an install
signed with a different key.
To sign properly, replace the signingConfig line with a config reading a keystore, and
keep the keystore out of the repository:
signingConfigs {
create("upload") {
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "upload.jks")
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
}
}
*.jks and *.keystore are already in .gitignore.
Releasing
Tagging is what publishes. The workflow builds and attaches app-release.apk:
git tag v0.2.0
git push origin v0.2.0
Bump versionCode and versionName in app/build.gradle.kts in the same commit as the
change that warrants them — versionCode must increase monotonically or Android refuses
the upgrade.
Why versions are pinned in a catalogue
Everything lives in gradle/libs.versions.toml. AGP,
Kotlin and the Compose compiler plugin move together: the Compose plugin version must
equal the Kotlin version, and the Compose BOM has to be one released against that
Kotlin. Bump them as a set, not individually.