Skip to content

Instantly share code, notes, and snippets.

@TheDancerCodes
Created October 11, 2018 16:50
Show Gist options
  • Save TheDancerCodes/408a6780c5b36b00c3cd8be5b9c9e40e to your computer and use it in GitHub Desktop.
Save TheDancerCodes/408a6780c5b36b00c3cd8be5b9c9e40e to your computer and use it in GitHub Desktop.
This gist contains the build pipeline for one of our internal products using Circle CI, Firebase TestLab and GCP (Google Cloud Platform)
default: &defaults
docker:
- image: circleci/android:api-26-alpha
working_directory: ~/repo
environment:
TERM: dumb
# Customize the JVM maximuum heap limit
_JAVA_OPTIONS: "-Xmx2048m -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m"'
gcloud_config: &gcloud_config
working_directory: ~/repo
docker:
- image: google/cloud-sdk:latest
environment:
TERM: dumb
workspace: &workspace
~/repo
attach_debug_workspace: &attach_debug_workspace
attach_workspace:
at: *workspace
persist_firebase_workspace: &persist_firebase_workspace
persist_to_workspace:
root: *workspace
paths:
- firebase
# Google Services From Firebase (JSON)
export_gservices_key: &export_gservices_key
run:
name: Export Google Services key environment variable
command: echo 'export GOOGLE_SERVICES_JSON="$GOOGLE_SERVICES_JSON"' >> $BASH_ENV
export_failure_determinant: &failure_determinant
run:
name: Export Failure Determinant
when: on_success
command: |
echo 'export INSTRUMENT_TEST_ARTIFACT=exempted' >> $BASH_ENV
decode_gservices_key: &decode_gservices_key
run:
name: Decode Google Services JSON key
command: |
mkdir app/src/prod/release
mkdir app/src/prod/debug
mkdir app/src/mock/debug
mkdir app/src/mock/release
touch app/src/prod/release/google-services.json
touch app/src/prod/debug/google-services.json
touch app/src/mock/release/google-services.json
touch app/src/mock/debug/google-services.json
touch app/google-services.json
echo $GOOGLE_SERVICES_JSON | base64 --decode >> app/src/mock/debug/google-services.json
echo $GOOGLE_SERVICES_JSON | base64 --decode >> app/src/mock/release/google-services.json
echo $GOOGLE_SERVICES_JSON | base64 --decode >> app/src/prod/debug/google-services.json
echo $GOOGLE_SERVICES_JSON | base64 --decode >> app/src/prod/release/google-services.json
echo $GOOGLE_SERVICES_JSON | base64 --decode >> app/google-services.json
# Google Cloud Service Account Key
export_gcloud_key: &export_gcloud_key
run:
name: Export Google Cloud Service Account Key Environment Variable
command: echo 'export GCLOUD_SERVICE_KEY="$GCLOUD_SERVICE_KEY"' >> $BASH_ENV
decode_gcloud_key: &decode_gcloud_key
run:
name: Decode Google Cloud Credentials
command: |
touch client-secret.json
echo $GCLOUD_SERVICE_KEY | base64 --decode >> client-secret.json
persist_debug_workspace: &persist_debug_workspace
persist_to_workspace:
root: *workspace
paths:
- app/build/intermediates
- app/build/outputs/androidTest-results
- app/build/outputs/apk
- app/build/outputs/code-coverage
- app/build/test-results
version: 2
jobs:
android_lint:
<<: *defaults
steps:
- checkout
# setup Keystore Environment for downloading necessary assets to use for deployment
- run:
name: Setup Keystore Environment
command: |
bash ~/repo/.circleci/keystore_setup.sh
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle" }}
- *export_gservices_key
- *decode_gservices_key
# Running static analysis tools
- run:
name: Running quality checks
command: |
echo "Run android lint"
cd ~/repo
./gradlew lint
# Storing reports
- store_artifacts:
path: ~/repo/app/build/reports
# Sending notification
- run:
name: Notifying slack channel (succeeded)
when: on_success
command: |
bash ~/repo/.circleci/notify_slack.sh
- run:
name: Notifying slack channel (failed)
when: on_fail
command: |
bash ~/repo/.circleci/notify_slack_fail.sh
test_unit:
<<: *defaults
steps:
- checkout
# setup Keystore Environment for downloading necessary assets to use for deployment
- run:
name: Setup Keystore Environment
command: |
bash ~/repo/.circleci/keystore_setup.sh
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle" }}
- *export_gservices_key
- *decode_gservices_key
# Running unit tests
- run:
name: Running unit tests on source code
command: |
cd ~/repo
./gradlew test
ls -a app/build/outputs/logs
- *persist_debug_workspace
#Storing unit test reports
- store_artifacts:
path: ~/repo/app/build/reports
destination: reports
- store_artifacts:
path: ~/repo/app/build/outputs/androidTest-results
destination: reports
# Sending notification
- run:
name: Notifying slack channel on succeed
when: on_success
command: |
bash ~/repo/.circleci/notify_slack.sh
- run:
name: Notifying slack channel on fail
when: on_fail
command: |
bash ~/repo/.circleci/notify_slack_fail.sh
## Build APK depending on current branch and instrumented test APK
deploy_build_job:
<<: *defaults
steps:
- checkout
# setup Keystore Environment for downloading necessary assets to use for deployment
- run:
name: Setup Keystore Environment
command: |
bash ~/repo/.circleci/keystore_setup.sh
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle" }}
- *export_gservices_key
- *decode_gservices_key
- run:
name: Gradle build (debug)
command: |
bash ~/repo/.circleci/gradle_build.sh
- *persist_debug_workspace
- store_artifacts:
path: app/build/outputs/apk/
destination: /apk/
- store_artifacts:
path: ~/repo/app/build/reports
destination: reports
# Sending notification
- run:
name: Notifying slack channel on succeed
when: on_success
command: |
bash ~/repo/.circleci/notify_slack.sh
- run:
name: Notifying slack channel on fail
when: on_fail
command: |
bash ~/repo/.circleci/notify_slack_fail.sh
## Run instrumentation tests
test_instrumentation:
<<: *gcloud_config
steps:
- checkout
- *attach_debug_workspace
- *export_gcloud_key
- *decode_gcloud_key
- *failure_determinant
- run:
name: Set Google Cloud target project
command: gcloud config set project <name-of-GCP-project>
- run:
name: Authenticate with Google Cloud
command: gcloud auth activate-service-account <name-of-GCP-app>.iam.gserviceaccount.com --key-file client-secret.json && gcloud firebase test android models list
- run:
name: Install Java JDK8
command: |
bash ~/repo/.circleci/setup_java.sh
- run:
name: Notifying slack channel on fail
when: on_fail
command: |
if [ "${INSTRUMENT_TEST_ARTIFACT}" == exempted ]; then
bash ~/repo/.circleci/notify_slack_fail.sh
fi
- run:
name: Modify Failure Determinant
when: on_success
command: |
echo 'export INSTRUMENT_TEST_ARTIFACT=required' >> $BASH_ENV
- run:
name: Run test on Firebase Test Lab with Flank
when: on_success
command: |
cat ~/repo/.circleci/config.properties >> /root/repo/config.properties
curl -O https://jitpack.io/com/github/TestArmada/flank/v2.0.3/flank-v2.0.3.jar
java -jar /root/repo/flank-v2.0.3.jar -a ~/repo/app/build/outputs/apk/mock/debug/app-mock-debug.apk -t ~/repo/app/build/outputs/apk/androidTest/mock/debug/app-mock-debug-androidTest.apk > result
cat result
cat result | grep -o 'gs://[^"]*' | grep 'gs://<name-of-GCP-app>-[a-z0-9.-]*' | head -1 > artifact_url
- run:
name: Create directory to store test results
command: mkdir firebase
- run:
name: Download instrumented test results from Firebase Test Lab on success
command: |
python ~/repo/.circleci/get_artifact_path.py
artifact_path="$(cat artifact_path)"
gsutil -m cp -n -r gs://${artifact_path}* /root/repo/firebase/ || true
- run:
name: Print firebase error results
when: on_fail
command: |
file="$(ls result)"
if [ $file == "result" ]; then
cat result
else
echo "Firebase did not create any output"
fi
- *persist_firebase_workspace
- store_artifacts:
when: on_success
path: firebase/
destination: /firebase/
# Sending notification
- run:
name: Notifying slack channel on succeed
when: on_success
command: |
bash ~/repo/.circleci/notify_slack.sh
- run:
name: Download instrumented test results from Firebase Test Lab on fail
when: on_fail
command: |
if [ "$INSTRUMENT_TEST_ARTIFACT" == required ]; then
cat result | grep -o 'gs://[^"]*' | grep 'gs://<name-of-GCP-app>-[a-z0-9.-]*' | head -1 > artifact_url
mkdir firebase
python ~/repo/.circleci/get_artifact_path.py
artifact_path="$(cat artifact_path)"
gsutil -m cp -n -r gs://${artifact_path}* /root/repo/firebase/ || true
fi
- store_artifacts:
when: on_fail
path: firebase/
destination: /firebase/
- run:
name: Notifying slack channel on fail
when: on_fail
command: |
if [ "$INSTRUMENT_TEST_ARTIFACT" == required ]; then
bash ~/repo/.circleci/notify_slack_fail.sh
fi
report_coverage:
<<: *defaults
steps:
- checkout
- run:
name: Setup Keystore Environment
command: |
bash ~/repo/.circleci/keystore_setup.sh
- restore_cache:
keys:
- v1-dependencies-{{ checksum "build.gradle" }}
- v1-dependencies-
- run:
name: Download dependencies
command: ./gradlew androidDependencies
- *attach_debug_workspace
- run:
name: Move Firebase Coverage Report
command: |
mkdir -p app/build/outputs/code-coverage/connected/flavors/MOCK
cp firebase/0/Nexus7-22-en-landscape/artifacts/coverage.ec app/build/outputs/code-coverage/connected/flavors/MOCK/coverage0.ec
cp firebase/1/Nexus7-22-en-landscape/artifacts/coverage.ec app/build/outputs/code-coverage/connected/flavors/MOCK/coverage1.ec
cp firebase/2/Nexus7-22-en-landscape/artifacts/coverage.ec app/build/outputs/code-coverage/connected/flavors/MOCK/coverage2.ec
cp firebase/3/Nexus7-22-en-landscape/artifacts/coverage.ec app/build/outputs/code-coverage/connected/flavors/MOCK/coverage3.ec
cp firebase/4/Nexus7-22-en-landscape/artifacts/coverage.ec app/build/outputs/code-coverage/connected/flavors/MOCK/coverage4.ec
- *export_gservices_key
- *decode_gservices_key
- run:
name: Code Climate Test Setup
command: |
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
- run:
name: Code Climate Pretest
command: ./cc-test-reporter before-build
- run:
name: Generate JaCoCo report
command: ./gradlew jacocoTestReport
- run:
name: Upload coverage to code climate
command: |
export JACOCO_SOURCE_PATH=app/src/main/java
cp app/src/prod/java/com/andela/mrm/Injection.java app/src/main/java/com/andela/mrm/Injection.java
./cc-test-reporter format-coverage app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml -t jacoco
./cc-test-reporter upload-coverage
- store_artifacts:
path: app/build/reports/
destination: /reports/
# Sending notification
- run:
name: Notifying Slack Channel on Succeed
when: on_success
command: bash ~/repo/.circleci/notify_slack.sh
- run:
name: Notifying slack channel on fail
when: on_fail
command: bash ~/repo/.circleci/notify_slack_fail.sh
workflows:
version: 2
lint_test_and_deployment:
jobs:
- android_lint
- test_unit:
requires:
- android_lint
- deploy_build_job:
requires:
- test_unit
- test_instrumentation:
requires:
- deploy_build_job
- report_coverage:
requires:
- test_instrumentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment