Configure CircleCI to build and sign an Android app
2019-07-11
For the TonUINO Android app I wanted to setup a CI system that also creates github releases.
The initial configuration worked (and is triggered after I push a vX.Y.Z
tag to the repository) at first, but unfortunately an Android app must be signed before it can be deployed to and executed on an actual device.
So this is how I set up signing on CircleCI in addition to compilation, test execution and creating apk releases.
Signing
Create a keystore, then convert it to a base64 string to be able to upload it to CircleCI
$ base64 keystore.jks --wrap=0
Then store it as environment variable KEYSTORE in the CircleCI environment config.
After that, also store the keystore password, the key name and the key password as an environment config.
Working configuration
app/build.gradle
android {
compileSdkVersion 28
defaultConfig { ... }
signingConfigs {
ci {
storeFile file('keystore.jks')
storePassword System.getenv('TONUINO_KEYSTORE_PASSWORD')
keyAlias = System.getenv('TONUINO_KEY')
keyPassword System.getenv('TONUINO_KEY_PASSWORD')
}
}
buildTypes {
release {
minifyEnabled false
signingConfig signingConfigs.ci
}
}
}
.circleci/config.yml
version: 2
jobs:
build:
working_directory: ~/code
docker:
- image: circleci/android:api-28-alpha
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Chmod permissions #if permission for Gradlew Dependencies fail, use this.
command: sudo chmod +x ./gradlew
- run:
name: Download Dependencies
command: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Tests
command: ./gradlew lint test
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: app/build/test-results
- run:
name: write keystore.jks # from environment variable
command: echo $TONUINO_KEYSTORE | base64 -di | tee keystore.jks app/keystore.jks >/dev/null
- run:
name: Build apk
command: ./gradlew assembleRelease
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: app/build/reports
destination: reports
- run:
name: Copy apk
command: |
mkdir -p dist
cp -r app/build/outputs/apk/release/*.apk dist/tonuino-nfc-tools.apk
- store_artifacts:
path: dist
destination: dist
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
# Persist the specified paths (workspace/echo-output) into the workspace for use in downstream job.
- persist_to_workspace:
# Must be an absolute path, or relative path from working_directory. This is a directory on the container which is
# taken to be the root directory of the workspace.
root: ./
# Must be relative path from root
paths:
- dist
publish-github-release:
docker:
# based off alpine docker image
- image: cibuilds/github:0.10
steps:
- attach_workspace:
at: persisted
- run:
name: "list files"
command: |
apk add tree
pwd
tree persisted
- run:
name: "Publish Release on GitHub"
command: |
VERSION=${CIRCLE_TAG}
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} persisted/dist
workflows:
version: 2
build-and-deploy:
jobs:
- build:
filters: # required since `deploy` has tag filters AND requires `build`
tags:
only: /.*/
- publish-github-release:
requires:
- build
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/