Configure Firebase project for Continuous Integration builds

Tam H. Doan
ProAndroidDev
Published in
5 min readAug 3, 2019

TL;DR: If you want to use Continuous Integration without exposing your production google-services.json in public Git repository, create new Build Variant and store a dummy google-services.json with fake API credentials.

Firebase (and Google Cloud Services) have become very popular with developers. It’s easy if you only develop your app on local machine, until you use some kind of Continuous Integration service (e.g: Travis CI).

What is google-services.json ?

From the Firebase documents:

Firebase manages all of your API settings and credentials through a single configuration file: google-service.json

In general, it’s safe to check it in to your Git repository. This JSON file does not contain any super-sensitive information (e.g: server API key).

Add google-services.json to your project. Source: Firebase

It contains information like: Database URL, Android key, storage bucket,.. These are not secret, but if your Security Rules & API Restrictions are not set up correctly attackers could use them against you.

Your Firebase / Google Cloud billing can be huge if your restriction failed!

According to many discussions on StackOverflow/Google+, it makes sense to add it to .gitignore and not include it in a public Git repo.

Firebase project with Continuous Integration

Because you’ve added google-services.json to .gitignore, it will not exist in your Git remote repo. When new CI build triggered, it will be FAILED with an error message like:

“File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it.”

There are some approaches to resolve this:

  • Use Travis Encrypting Files to encrypt your production google-services.json.
  • Create new Build Variant for your builds on CI service.
  • Upload production google-service.json, but enable Security Rules & API Restrictions to protect your Firebase/Google Cloud account.

The third approach is not recommended. Many projects — like googlesamples/google-services — does have it in its .gitignore . So we will only talk about first two approach.

First approach: Use Travis Encrypting Files

Use this command: travis encrypt-file google-services.json

This will encrypt your file using a symmetric encryption (AES-256) and store the secret in a secure variable (openssl aes-256-cbc key).

$ travis encrypt-file google-services.jsonencrypting google-services.json for nhoxbypass/travis-encrypt-ex
storing result as google-services.json.enc
storing secure env variables for decryption

Add the secret key to your build script (.travis.yml), add encrypted google-services.json.enc to your Git repo. Then Travis CI will be able to decrypt your file when execute build. See documents.

Second approach: Configure Build Variants for CI

Create new Build Variant and store a dummy google-services.json — with fake API credentials — that will be used for your builds on CI service.

What is Build Variant?

Each Build Variant represents a different version of your app that you can build.

Ex: you might want to build a free version of your app (with limited features), and another paid version (with full features).

Build Variants are the result of Gradle using a specific set of rules to combine settings, code, resources configured in your build types and product flavors. You can configure them in app/build.gradle, inside the android block:

Default build variants that automatically created by Android Studio

Create new development Build Variant for CI builds

As I said above, we will create new Build Variants. Let’s call them mock and prod (use mock instead of dev because we may use it to support mock testing in the future).

Add these build variants by updating buildTypes & productFlavors like:

Gradle config with 4 build variants

After Gradle synced, create a dummy google-services.json inside app/src/mock package:

app/
├── src/
│ ├── main/
│ └── mock/
│ └── google-services.json (dummy)
├── google-services.json
└── build.gradle

Update this file with the fake content below. Remember to replace com.yourapp.packagename with your app package name.

Dummy google-services.json with fake API credentials

Now we’ve successfully added new variant with dummy config file.

Real-production google-services.json with dummy google-services.json

But how can build tools know where the build being executed — on CI service or just local machine — to select a proper variant?

We specify build parameter -Pbuild. Update build script in .travis.yml:

script:  
- ./gradlew clean build -Pbuild=devCI

The -Pbuild=devCI tells build tools to only select & build mock variant using our dummy app/src/mock/google-services.json. Trigger CI build again, you will see it PASSED. Everything is done in just a few line of code!

Optimize your build speed

From now on, the project have 4 variants, and the build process seem to be slowed down, you can monitor it with Gradle build scan:

Why MockRelease and MockDebug is being built together?

Because command ./gradlew build will build all variants of your project! To ignore mock when -Pbuild=devCI not specified, update app/build.gradle

Task to ignore unused build variants

Why recommended the second approach?

This approach is recommended because sometimes your project use other cloud services (e.g: Facebook login SDK), then you will have other API keys to secure. In this case, you can store these keys in a secrets.xml, then upload dummy one to Git repo in mock package:

app/
├── src/
├── main/res/values/secrets.xml
└── main/res/values/secrets.xml (dummy)

Furthermore, this approach is not limited to CI and can be extended for your production build also. Ex: When you require different production google-services.json or different AndroidManifest.xml (with some specific properties) for your free and paid version of your app.

But, what about UI testing on CI?

Yes, this solution has one drawback. Because we use dummy config files with fake API credentials, so when deploy the app into emulators for automation UI testing, it will not work properly and the test will FAILED.

In that case you can use middleware to mount these config files on CI service. Or just generate another google-services.json file for mock variant. This is a real config file from Firebase, but you only use for CI building and testing. You can disable it anytime without affecting your production app.

App signed with development config & app production config

In conclusion

When working with Firebase (and Google Cloud Services), you might want to trigger build on Continuous Integration service without exposing your production google-services.json in public Git repository. In that case the build will FAILED.

To resolve this issue, use Travis Encrypting Files to encrypt it. Or create new Build Variant and store a dummy google-services.json — with fake API credentials — that will be used for your builds on CI service.

Happy coding~

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Tam H. Doan

Software Engineer, also a Writer and Runner

Responses (2)

Write a response