From c92148d2da8ec50b6704795f7cb14ef59d09159f Mon Sep 17 00:00:00 2001 From: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co> Date: Mon, 13 Jul 2026 14:46:04 -0700 Subject: [PATCH] BOT-1247 Configure Android Play identity and signing Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co> --- mobile/README.md | 13 ++++ mobile/android/app/build.gradle.kts | 68 +++++++++++++++++-- .../android/app/src/main/AndroidManifest.xml | 1 + .../block/buzz/mobile}/MainActivity.kt | 2 +- 4 files changed, 77 insertions(+), 7 deletions(-) rename mobile/android/app/src/main/kotlin/{com/buzz/buzz_mobile => xyz/block/buzz/mobile}/MainActivity.kt (99%) diff --git a/mobile/README.md b/mobile/README.md index d1f455e7f8..1a6ae19ab6 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -29,6 +29,19 @@ flutter test Or from the repo root: `just mobile-check` and `just mobile-test`. +## Android release signing + +Android release builds fail unless all upload-key inputs are supplied through the +environment: + +- `BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH`: path to a CI-vended keystore file +- `BUZZ_ANDROID_UPLOAD_KEYSTORE_PASSWORD` +- `BUZZ_ANDROID_UPLOAD_KEY_ALIAS` +- `BUZZ_ANDROID_UPLOAD_KEY_PASSWORD` + +The keystore path must be absolute, and the keystore must remain outside the +repository. Development and debug builds do not require these variables. + ## Architecture ``` diff --git a/mobile/android/app/build.gradle.kts b/mobile/android/app/build.gradle.kts index 467fa22326..c29158e50c 100644 --- a/mobile/android/app/build.gradle.kts +++ b/mobile/android/app/build.gradle.kts @@ -5,8 +5,22 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } +val uploadKeystorePath = providers.environmentVariable("BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH").orNull +val uploadKeystorePassword = providers.environmentVariable("BUZZ_ANDROID_UPLOAD_KEYSTORE_PASSWORD").orNull +val uploadKeyAlias = providers.environmentVariable("BUZZ_ANDROID_UPLOAD_KEY_ALIAS").orNull +val uploadKeyPassword = providers.environmentVariable("BUZZ_ANDROID_UPLOAD_KEY_PASSWORD").orNull +val uploadSigningValues = + mapOf( + "BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH" to uploadKeystorePath, + "BUZZ_ANDROID_UPLOAD_KEYSTORE_PASSWORD" to uploadKeystorePassword, + "BUZZ_ANDROID_UPLOAD_KEY_ALIAS" to uploadKeyAlias, + "BUZZ_ANDROID_UPLOAD_KEY_PASSWORD" to uploadKeyPassword, + ) +val missingUploadSigningValues = uploadSigningValues.filterValues { it.isNullOrBlank() }.keys +val hasUploadSigning = missingUploadSigningValues.isEmpty() + android { - namespace = "com.buzz.buzz_mobile" + namespace = "xyz.block.buzz.mobile" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -20,8 +34,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.buzz.buzz_mobile" + applicationId = "xyz.block.buzz.mobile" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion @@ -30,11 +43,54 @@ android { versionName = flutter.versionName } + signingConfigs { + if (hasUploadSigning) { + create("upload") { + storeFile = file(requireNotNull(uploadKeystorePath)) + storePassword = uploadKeystorePassword + keyAlias = uploadKeyAlias + keyPassword = uploadKeyPassword + } + } + } + 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") + if (hasUploadSigning) { + signingConfig = signingConfigs.getByName("upload") + } + } + } +} + +gradle.taskGraph.whenReady { + val buildsRelease = allTasks.any { task -> + task.project == project && task.name in setOf("assembleRelease", "bundleRelease") + } + if (buildsRelease && !hasUploadSigning) { + throw GradleException( + "Release builds require Android upload signing credentials. Missing: " + + missingUploadSigningValues.sorted().joinToString(", "), + ) + } + if (buildsRelease) { + val configuredKeystore = File(requireNotNull(uploadKeystorePath)) + if (!configuredKeystore.isAbsolute) { + throw GradleException( + "BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH must be absolute: $configuredKeystore", + ) + } + val keystore = file(configuredKeystore) + val repositoryRoot = rootProject.projectDir.parentFile.parentFile.canonicalFile + if (keystore.canonicalFile.toPath().startsWith(repositoryRoot.toPath())) { + throw GradleException( + "BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH must be outside the repository: $keystore", + ) + } + if (!keystore.isFile || !keystore.canRead()) { + throw GradleException( + "BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH is not a readable file: $keystore", + ) } } } diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index c362edc926..1bc1e1edb6 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ +