Circle CI and gradle.properties

gradlephant gradle logo

 

circle-ci-logo

 

 

 

 

 

 

Recently I was working on getting a Circle CI build up and running for an Android project, and ran into a bit of trouble getting it to work with my `gradle.properties` file.  Here’s an overview of the issue I experienced, and how I solved it.

What is Circle CI?

Circle CI is a continuous integration tool like Jenkins or Travis, which allows you to automate builds and tests in the cloud. You can automate builds to run on every push to certain branches or pull request, so you can maintain quality code. It’s pretty awesome! Circle CI is a relatively new player in the market, adding support for Android just over a year ago.

What is gradle.properties?

If your app has any secret credentials like API keys or other authentication data, you should never check this information into version control where somebody else can access it. You should instead keep it secret by only referencing it via a local file.  In Android development, this is typically stored in a file named `gradle.properties`, which lives in your `~/.gradle/gradle.properties` directory.

You store your credentials in the file like this:

`MY_API_KEY=123456`

Dynamically create Strings from them in your `build.gradle` file like this:

`buildConfigField(“String”, “MY_API_KEY_STRING”, “\”” +MY_API_KEY + “\””)`

And access these Strings in your Java code like this:

`BuildConfig.MY_API_KEY_STRING`

The issue: How to achieve consistent access to credentials?

The last CI tool I used (Jenkins) allowed developers to create and upload a `gradle.properties` file to their account, and this file was referenced by their Jenkins builds.  This file was stored in a secure location, so credentials were kept safe.  Unfortunately, Circle CI doesn’t have a similar configuration option to just upload your file.  Instead, Circle CI requires you to store credentials and other secret information as environment variables for your project.

So, I just needed to figure out a way to link the environment settings on Circle CI with the values in environment variables. I really didn’t want to add logic in my `build.gradle` file to look in different places depending on if it is a local or CI build.  I just wanted my project to always look for credentials in the same spot; it shouldn’t have to know if it is a CI build or not. More importantly, I’d like my CI build to be as close to my local build as possible, because consistable builds are important! 😉

https://twitter.com/5280mark/status/626806385729212420?lang=en

Happy Ending: Circle CI & gradle.properties live in harmony

I decided I could just set the environment variables in the Circle CI GUI, and then write a shell script which would run prior to building only for CI, and copy environment variables into a `gradle.properties` file on the CI instance.

First, in the Circle CI GUI, I went to my project, then selected “Project Settings” in the upper right hand corner. In the menu on the left side I clicked “Environment variables” which is under the “Tweaks” header. Here I added my credentials as a name value pair.

Next, I wrote the bash script `environmentSetup.sh`, and placed it in the top level directory of my project.  I have put the script I wrote in a gist, to save you some time.  Here is the method which does the work:

function copyEnvVarsToGradleProperties {
    GRADLE_PROPERTIES=$HOME"/.gradle/gradle.properties"
    export GRADLE_PROPERTIES
    echo "Gradle Properties should exist at $GRADLE_PROPERTIES"

    if [ ! -f "$GRADLE_PROPERTIES" ]; then
        echo "Gradle Properties does not exist"

        echo "Creating Gradle Properties file..."
        touch $GRADLE_PROPERTIES

        echo "Writing TEST_API_KEY to gradle.properties..."
        echo "TEST_API_KEY=$TEST_API_KEY_ENV_VAR" >> $GRADLE_PROPERTIES
    fi
}

Random tip, I like to put tons of `echo`s in any script that is running in CI only.  Sometimes CI can be a black box when things go wrong, and trial and error debugging is a huge pain.  By front-loading the print statements, I will save myself a little time if something goes wrong in the future.

Lastly, I needed to tell Circle CI to actually run this file. Any custom Circle CI configurations should be written in a `circle.yml` file which also lives at the top level directory of your project.  I needed the bash script to run before anything else, so my gradle build has all the info it needs when it starts building.  For this to happen, I listed the shell script as a pre-build dependency:

dependencies:
  pre:
    - source environmentSetup.sh && copyEnvVarsToGradleProperties

Now, I can continue to access API keys in `build.gradle` just as I always have:

buildConfigField("String", "THIS_TEST_API_KEY", """ + TEST_API_KEY + """)

My Android project always references `gradle.properties` for credentials, whether its a local or CI build, and my credentials are stored securely in Circle CI’s environment variables dashboard. Yay!

Fun fact: While I was working on this article, I started wondering about where the cute Gradle elephant logo came from. I tweeted my question at Gradle, and they tweeted back!  Love it!

This entry was posted in Android, Circle CI, Continuous Integration, Gradle. Bookmark the permalink.

1 Response to Circle CI and gradle.properties

  1. Brajendra says:

    Wow such a wonderful tutorial , it helps all my problem with gradle and CircleCi, excluding the following one,
    TEST_API_KEY
    i can’t able to find this variable on my local build, can you please upload a sample project.

Leave a Reply

Your email address will not be published. Required fields are marked *