Building

July 30, 2026 · View on GitHub

Toolchain

JDK17 or later
Android SDKcompileSdk 36, minSdk 24
Gradle8.14.3, via the committed wrapper
AGP8.13.2
Kotlin2.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.