Circle CI & Android: 8 advanced configuration tips

Ideas to improve your Circle CI configuration on android projects

Maxi Rosson
Dec 14, 2020 · 5 min read

1. Define executors for each resource class

Sometimes you need to quickly change the resource class you use on your job’s executor. For example, you could need to do that to measure the performance impact or because you need more memory/CPU.

You can simplify those changes by defining one per resource class. Each executor could have for example custom GRADLE_OPTS defined.

references:  android_config: &android_config
working_directory: "/path"
docker:
- image: circleci/android:api-30
executors: android_executor_small:
<<
: *android_config
resource_class: small
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx2g -XX:MaxPermSize=2g"
android_executor_medium:
<<
: *android_config
resource_class: medium
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4g -XX:MaxPermSize=4g"
android_executor_medium_plus:
<<
: *android_config
resource_class: medium+
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxPermSize=6g"

2. Unify Gradle user home path between different docker images

Not all the docker images use the same default user home path. Then, the Gradle user's home path can change between jobs. This can cause problems when saving/restoring caches on jobs with different home paths.

Some docker images allow you to override the Gradle user's home path.

parameters:  home:
type: string
default: /home/circleci
executors: basic_executor:
working_directory: << pipeline.parameters.home >>/project
resource_class: small
docker:
- image: cimg/base:stable
environment:
GRADLE_USER_HOME: << pipeline.parameters.home >>/.gradle

3. Cache versioning

Versioning each key could be useful in certain situations:

  • If you need to clean up your cache, you can easily do that by just incrementing the cache key version number.
  • If you need to change the cache content to something not backward compatible with the previous one, you can increment the cache key version number and avoid conflicts.
parameters:
cacheKey:
type: string
default: v1-cache
...- save_cache:
key: << pipeline.parameters.cacheKey >>
paths:
- /path
- restore_cache:
keys:
- << pipeline.parameters.cacheKey >>

4. Job caches instead of attach to workspace

You can have only one workspace per workflow to persist your files. If you have multiple big files persisted by different jobs, the attach_workspace step could take time. Sometimes a job doesn’t need to attach all the files on the workspace.

To solve that problem, you can use multiple caches instead of the workspace. You just need to use the pipeline & workflow ids as part of the cache key.

parameters:
jobCacheKey:
type: string
default: v1-jobcache
commands:
save_job_cache
:
parameters:
key:
type: string
default: ""
path
:
type: string
default: ""
steps
:
- save_cache:
key: << pipeline.parameters.jobCacheKey >>-<< parameters.key >>-<< pipeline.id >>-{{ .Environment.CIRCLE_WORKFLOW_ID }}
paths:
- << parameters.path >>
restore_job_cache:
parameters:
key:
type: string
default: ""
steps
:
- restore_cache:
keys:
- << pipeline.parameters.jobCacheKey >>-<< parameters.key >>-<< pipeline.id >>-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- << pipeline.parameters.jobCacheKey >>-<< parameters.key >>-<< pipeline.id >>

5. Store Junit Tests results

CircleCI collects from XML files and uses it to provide insights into your job.

Tests summary on CircleCI

To achieve that you just need to run the store_test_results step to upload the XML files after executing your Junit tests.

- run:
name:
Save test results
command: |
mkdir -p ~/test-results/junit/
find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/test-results/junit/ \;
when: always
- store_test_results:
path: ~/test-results

6. Store Firebase Test Lab results

You can also see the Firebase Test Lab Tests summary in the same way you can see Junit Tests.

You will need to send the results-dir flag to the gcloud command, so the Firebase Test Lab device can store the XML with the test results on that directory.

Then you can download the XML with the results of the tests, using the gsutil command.

- run:
name: Run Instrumentation tests
command: |
resultsDir="$(date -u +%F_%T.%3N)_app"
mkdir -p "app/build/testlab"
echo "${resultsDir}" >> app/build/testlab/resultsDir.txt

gcloud firebase test android run
... // YOUR SPECIFIC FLAGS HERE
--results-dir=${resultsDir}
- run:
name: Download test XML results
command: |
resultsDir=$(cat app/build/testlab/resultsDir.txt)
gsutil cp gs://${BUCKET_NAME}/${resultsDir}/**/test_result_1.xml app/build/testlab
when: always
- store_test_results:
path: app/build/testlab

7. Switch to Gradle Binary Distribution

In your development environment, you would like to use the -all Gradle distribution on your gradle-wrapper.properties file, so your IDE has code-completion enabled and you can navigate to the Gradle source code.

But using -all increases your Gradle download times. So, using -bin in Circle CI is a better idea, because you don’t need the Gradle sources there.

The following step switches to Gradle Binary Distribution. It should be executed after the checkout step and before any Gradle execution.

- checkout
- run:
name: Switch to Gradle Binary Distribution
command: sed -i -e 's/-all.zip/-bin.zip/' gradle/wrapper/gradle-wrapper.properties

8. Verify the integrity of the Gradle Wrapper JAR

The Wrapper JAR is a binary file that will be executed on the computers of developers and build servers. As with all such files, you should be sure that it’s trustworthy before executing it. Since the Wrapper JAR is usually checked into a project’s version control system, there is the potential for a malicious actor to replace the original JAR with a modified one by submitting a pull request that seemingly only upgrades the Gradle version. you can find more official information about this topic.

The following step verifies the checksum of the Wrapper JAR to ensure that it has not been tampered. You should execute it on each workflow.

- run:
name: Verify the integrity of the Gradle Wrapper JAR
command: |
cd gradle/wrapper
gradleVersion=$(grep "distributionUrl" gradle-wrapper.properties | grep -Po "(\d+\.)+\d+")
curl --location --output gradle-wrapper.jar.sha256
echo " gradle-wrapper.jar" >> gradle-wrapper.jar.sha256
sha256sum --check gradle-wrapper.jar.sha256
Follow us for more productivity tools & ideas for Android, Kotlin & Gradle projects.

Dipien

Boost your Productivity

Maxi Rosson

Written by

Developer Productivity Engineer | Android | Productivity tools & ideas for Android, Kotlin & Gradle developers on medium.dipien.com

Dipien

Dipien

Productivity tools & ideas for Android, Kotlin & Gradle developers.

Maxi Rosson

Written by

Developer Productivity Engineer | Android | Productivity tools & ideas for Android, Kotlin & Gradle developers on medium.dipien.com

Dipien

Dipien

Productivity tools & ideas for Android, Kotlin & Gradle developers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface.

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox.

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store