fix(android): sign release APK with a stable keystore in CI

Release builds were signed with the debug keystore. On the GitHub runner
that keystore is generated fresh on every build, so each CI APK carried a
different signature and Android refused to install it over an existing
version ("App not installed").

- build.gradle.kts: read android/key.properties when present and use it
  as the release signing config; fall back to debug otherwise so local
  `flutter run --release` keeps working.
- build-apk.yml: decode the keystore from ANDROID_KEYSTORE_* secrets into
  android/key.properties before building, and fail early if any secret
  is missing instead of silently signing with a throwaway key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3E5JovvDaQQ19D4UR258o
This commit is contained in:
MichaelandClaude Fable 5 committed 2026-08-30 16:21:51 +02:00
1 parent 05ca6f85be
commit c096532fec
2 files changed
+54 -3

No files matched your search

+30 -3
View File
@@ -1,3 +1,5 @@
import java.util.Properties
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,6 +7,18 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
// Signature release stable : android/key.properties (ignoré par git) pointe vers
// un keystore fixe. Sans ce fichier, on retombe sur la clé debug locale pour que
// `flutter run --release` continue de marcher. La CI DOIT fournir key.properties,
// sinon chaque build serait signé avec une clé debug aléatoire et l'APK ne
// s'installerait pas par-dessus une version existante.
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
val hasReleaseKeystore = keystorePropertiesFile.exists()
if (hasReleaseKeystore) {
keystorePropertiesFile.inputStream().use { keystoreProperties.load(it) }
}
android {
namespace = "com.example.xtremflow"
compileSdk = flutter.compileSdkVersion
@@ -30,11 +44,24 @@ android {
versionName = flutter.versionName
}
signingConfigs {
if (hasReleaseKeystore) {
create("release") {
storeFile = file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
}
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
signingConfig = if (hasReleaseKeystore) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
}
}
}