We have several Firebase projects which share the same code base via build types and flavors. We aim to use the app distribution via Gradle and authenticate using service account credentials.
In the docs, it is shown that firebaseAppDistribution
block can be used to configure the parameters and the service credential file path is one of them. Since each variant is a Firebase project and each project has its own service credentials, as far as I can understand we need to point to separate service credential file paths in the Gradle configuration.
I've tried to update the file path with a gradle task depending on the variant but couldn't make it work. The current build file looks like:
...
apply plugin: 'com.google.firebase.appdistribution'
class StringExtension {
String value
StringExtension(String value) {
this.value = value
}
public void setValue(String value) {
this.value = value
}
public String getValue() {
return value
}
}
android {
...
productFlavors.whenObjectAdded {
flavor -> flavor.extensions.create("service_key_prefix", StringExtension, '')
}
productFlavors {
flavor1 {
...
service_key_prefix.value = "flavor1"
}
flavor2 {
...
service_key_prefix.value = "flavor2"
}
}
buildTypes {
debug {
firebaseAppDistribution {
releaseNotesFile = file("internal_release_notes.txt").path
groupsFile = file("group_aliases_debug_fb.txt").path
}
}
release {
firebaseAppDistribution {
releaseNotesFile = file("release_notes.txt").path
groupsFile = file("group_aliases_prod_fb.txt").path
}
}
}
}
...
android.applicationVariants.all { variant ->
task("firebaseCredentials${variant.name.capitalize()}", overwrite: true) {
variant.productFlavors.each { flavor ->
doLast {
firebaseAppDistribution {
def serviceKeyFile = file(
"../${flavor.service_key_prefix.value}-${variant.buildType.name}-service-key.json")
if (serviceKeyFile != null) {
serviceCredentialsFile = serviceKeyFile.path
}
}
}
}
}
}
android.applicationVariants.all { variant ->
def distTask = tasks.named("appDistributionUpload${variant.name.capitalize()}")
def configTask = tasks.named("firebaseCredentials${variant.name.capitalize()}")
distTask.configure {
dependsOn(configTask)
}
}
apply plugin: 'com.google.gms.google-services'
The tasks seem to run correctly but I guess the file path is not updated because it still gives the following error when I run appDistributionUpload
:
Could not find credentials. To authenticate, you have a few options:
Set the
serviceCredentialsFile
property in your gradle pluginSet a refresh token with the FIREBASE_TOKEN environment variable
Log in with the Firebase CLI
Set service credentials with the GOOGLE_APPLICATION_CREDENTIALS environment variable
Any ideas on how to achieve such distribution configuration?
You can't have two projects of the same package name. Even if you delete it. It will take a least 4-5 days to get deleted fully from the developer's console. So the only solution is to generate a new SHA-1 key by custom signing the app by generating a signed apk from the android studio.
Firebase App Distribution makes distributing your apps to trusted testers painless. By getting your apps onto testers' devices quickly, you can get feedback early and often. And if you use Crashlytics in your apps, you'll automatically get stability metrics for all your builds, so you know when you're ready to ship.
Open the App Distribution page of the Firebase console. Select your Firebase project when prompted. On the Releases page, select the app you want to distribute from the drop-down menu. Drag your app's APK file to the console to upload it.
Add the Firebase App Distribution Admin role. Create a private json key and move the key to a location accessible to your build environment. Be sure to keep this file somewhere safe, as it grants administrator access to App Distribution in your Firebase project.
On Android, Firebase automatically manages connection state to reduce bandwidth and battery usage. When a client has no active listeners, no pending write or onDisconnect operations, and is not explicitly disconnected by the goOffline method, Firebase closes the connection after 60 seconds of inactivity.
By default, 10MB of previously synced data is cached. This should be enough for most applications. If the cache outgrows its configured size, the Firebase Realtime Database purges data that has...
Caution: When you register your app with Firebase, make sure to enter the same package name as the app you're distributing. The package name value is case-sensitive and cannot be changed for your app in Firebase after it's registered with your Firebase project. Step 1. Set up your Android project
After contacting the support team, I've learned that this configuration is not available yet out of the box and exists as a feature request as of now.
Here is the workaround from the Firebase support team, which modifies the related environment variable in memory at the beginning of each upload task:
android {
applicationVariants.all { variant ->
final uploadTaskName = "appDistributionUpload${variant.name.capitalize()}"
final uploadTask = project.tasks.findByName(uploadTaskName)
if (uploadTask != null) {
uploadTask.doFirst {
resetCredentialsCache()
final value = "$projectDir/src/${variant.name}/app-distribution-key.json"
setEnvInMemory('GOOGLE_APPLICATION_CREDENTIALS', value)
}
}
}
}
private static void setEnvInMemory(String name, String val) {
// There is no way to dynamically set environment params, but we can update it in memory
final env = System.getenv()
final field = env.getClass().getDeclaredField("m")
field.setAccessible(true)
field.get(env).put(name, val)
}
private static void resetCredentialsCache() {
// Google caches credentials provided by environment param, we are going to reset it
final providerClass = Class.forName(
'com.google.firebase.appdistribution.buildtools.reloc.com.google.api.client.googleapis.auth.oauth2.DefaultCredentialProvider')
final providerCtor = providerClass.getDeclaredConstructor()
providerCtor.setAccessible(true)
final provider = providerCtor.newInstance()
final credsClass = Class.forName(
'com.google.firebase.appdistribution.buildtools.reloc.com.google.api.client.googleapis.auth.oauth2.GoogleCredential')
final field = credsClass.getDeclaredField('defaultCredentialProvider')
field.setAccessible(true)
field.set(null, provider)
}
EDIT: The above solution works only for app distribution plugin version 1.3.1.
I didn't contact the support further and used GOOGLE_APPLICATION_CREDENTIALS
environment variable on Jenkins CI, as explained here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With