Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to configure separate serviceCredentialsFile for each Android build variant with Firebase App Distribution?

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:

  1. Set the serviceCredentialsFile property in your gradle plugin

  2. Set a refresh token with the FIREBASE_TOKEN environment variable

  3. Log in with the Firebase CLI

  4. Set service credentials with the GOOGLE_APPLICATION_CREDENTIALS environment variable

Any ideas on how to achieve such distribution configuration?

like image 881
Emre Dirican Avatar asked Jan 02 '20 09:01

Emre Dirican


People also ask

How multiple developers can work on the same Android app connected to a single Firebase console?

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.

How does Firebase app distribution work?

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.

How do I download APK from Firebase app distribution?

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.

How do I add app distribution to my Firebase project?

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.

How does Firebase manage connection state on Android?

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.

How much previously synced data is cached in Firebase?

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...

Can I change the package name of my App in Firebase?

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


1 Answers

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

like image 194
Emre Dirican Avatar answered Oct 27 '22 17:10

Emre Dirican