Make the Android self-update actually install.

CI stopped publishing when the iOS job outgrew the macos-14 Xcode, so
the builds branch froze; the iOS job now runs on macos-26 and publish
keeps the previous package for any platform that failed, with
VERSION-<platform> markers naming the commit each package came from.
APKs are signed with a committed sideload key instead of the runner's
random debug key, so Android accepts them as updates, and the version
code grows with every CI run. The updater explains the unknown-sources
step, keeps the Update button after errors and checks the download is
this app.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-09-18 18:36:25 +02:00
commit e00ced808b
8 changed files with 143 additions and 36 deletions

View file

@ -5,6 +5,11 @@ plugins {
val commit: String = System.getenv("COMMIT")?.takeIf { it.isNotBlank() } ?: "dev"
// Every CI build must be installable over the previous one, so the version
// code grows with the workflow run and local builds stay below it.
val ciRun: Int? = System.getenv("GITHUB_RUN_NUMBER")?.toIntOrNull()
val appVersionCode: Int = ciRun?.let { 100 + it } ?: 7
android {
namespace = "org.maturita.maturita"
compileSdk = 37
@ -13,17 +18,39 @@ android {
applicationId = "org.maturita.maturita"
minSdk = 26
targetSdk = 35
versionCode = 6
versionName = "android-6"
versionCode = appVersionCode
versionName = "android-$appVersionCode"
buildConfigField("String", "COMMIT", "\"$commit\"")
buildConfigField("String", "UPDATE_REPO", "\"pepaskopec-blip/maturita.c\"")
buildConfigField("String", "UPDATE_BRANCH", "\"builds\"")
}
// Android only installs an update signed with the same key as the app
// already on the phone. The default debug keystore is generated per
// machine, so CI builds would never update each other; sign everything
// with the repository's sideload key instead. It is not a secret (this
// is a sideloaded open-source app), but a real key can be supplied
// through MATURITA_KEYSTORE / MATURITA_KEYSTORE_PASSWORD.
signingConfigs {
create("sideload") {
val ks = System.getenv("MATURITA_KEYSTORE")?.takeIf { it.isNotBlank() }
?: rootProject.file("keystore/sideload.jks").path
val pass = System.getenv("MATURITA_KEYSTORE_PASSWORD")?.takeIf { it.isNotBlank() }
?: "maturita-sideload"
storeFile = file(ks)
storePassword = pass
keyAlias = System.getenv("MATURITA_KEY_ALIAS")?.takeIf { it.isNotBlank() } ?: "maturita"
keyPassword = System.getenv("MATURITA_KEY_PASSWORD")?.takeIf { it.isNotBlank() } ?: pass
}
}
buildTypes {
debug {
signingConfig = signingConfigs.getByName("sideload")
}
release {
isMinifyEnabled = false
signingConfig = signingConfigs.getByName("debug")
signingConfig = signingConfigs.getByName("sideload")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",