version: 2.1 orbs: android: circleci/android@0.2.1 sonarcloud: sonarsource/sonarcloud@1.0.2 ####################### # Commands section # For code reuse. ####################### commands: install-ndk: android/install-ndk restore-android-build-cache: android/restore-build-cache save-android-build-cache: android/save-build-cache scan-sonar: sonarcloud/scan restore-gradle-cache: description: "Restore gradle caches" steps: - restore_cache: key: gradle-v1-{{ arch }}-{{ checksum "build.gradle" }}-{{ checksum "Corona-Warn-App/build.gradle" }}-{{ checksum "Server-Protocol-Buffer/build.gradle" }} save-gradle-cache: description: "Save gradle caches" steps: - save_cache: paths: - ~/.gradle key: gradle-v1-{{ arch }}-{{ checksum "build.gradle" }}-{{ checksum "Corona-Warn-App/build.gradle" }}-{{ checksum "Server-Protocol-Buffer/build.gradle" }} run-gradle-cmd: description: "Running gradle command with environment options" parameters: desc: type: string default: "Running gradle command" cmd: type: string steps: - run: name: << parameters.desc >> command: > ./gradlew -PdisablePreDex << parameters.cmd >> no_output_timeout: 30m environment: JVM_OPTS: -Xmx4096m GRADLE_OPTS: > -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false run-gradle-cmd-test-splitting: description: "Running gradle command with environment options and test splitting" parameters: desc: type: string default: "Running gradle command" cmd: type: string steps: - run: name: Test splitting output command: circleci tests glob "**/test*/**/*.kt" | circleci tests split | xargs -n 1 echo - run: name: << parameters.desc >> command: > ./gradlew -PdisablePreDex << parameters.cmd >> -i -PtestFilter="`circleci tests glob "**/test*/**/*.kt" | circleci tests split`" environment: JVM_OPTS: -Xmx4096m GRADLE_OPTS: > -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false skip-for-external-pull-requests: description: "Skip for external pull requests due to missing access to secrets." steps: - run: name: Early return if this build is from a forked PR command: | if [ -n "$CIRCLE_PR_NUMBER" ]; then echo "Nothing to do for forked PRs, so marking this step successful" circleci step halt fi compress-path: parameters: input: type: string output: type: string description: "Compress << parameters.input >> to << parameters.output >>" steps: - run: name: Compress files command: > zip -r << parameters.output >> << parameters.input >> setup-google-cloud: description: "Setup Google Cloud access." steps: - run: name: Store Google Service Account command: echo $GCLOUD_SERVICE_KEY_BASE64 | base64 -di > ${HOME}/gcloud-service-key.json - run: name: Authorize gcloud and set config defaults command: | sudo gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json sudo gcloud --quiet config set project ${GOOGLE_PROJECT_ID} ####################### # Jobs section # Tasks that get executed ####################### jobs: detekt: executor: android/android resource_class: large working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Detekt check cmd: ":Corona-Warn-App:detekt" - store_artifacts: path: Corona-Warn-App/build/reports destination: reports quick_build_device_release_no_tests: executor: android/android resource_class: xlarge working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Quick Build cmd: "assembleDeviceRelease" - save-gradle-cache - save-android-build-cache - store_artifacts: path: Corona-Warn-App/build/reports destination: reports quick_build_device_for_testers_release_no_tests: executor: android/android resource_class: xlarge working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Quick Build cmd: ":Corona-Warn-App:assembleDeviceForTestersRelease" - save-gradle-cache - save-android-build-cache - store_artifacts: path: Corona-Warn-App/build/reports destination: reports unit_tests_device_release: executor: android/android resource_class: xlarge working_directory: ~/project parallelism: 3 steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd-test-splitting: desc: Unit tests cmd: ":Corona-Warn-App:testDeviceReleaseUnitTest -i" - save-gradle-cache - save-android-build-cache - store_test_results: path: Corona-Warn-App/build/test-results - persist_to_workspace: root: /home/circleci paths: - ./project - compress-path: input: ./Corona-Warn-App/build/reports output: /tmp/unit_tests_device_release.zip - store_artifacts: path: /tmp/unit_tests_device_release.zip destination: zips/unit_tests_device_release.zip unit_tests_device_for_testers_release: executor: android/android resource_class: xlarge working_directory: ~/project parallelism: 3 steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd-test-splitting: desc: Unit tests with splitting cmd: ":Corona-Warn-App:testDeviceForTestersReleaseUnitTest" - save-gradle-cache - save-android-build-cache - store_test_results: path: Corona-Warn-App/build/test-results - compress-path: input: ./Corona-Warn-App/build/reports output: /tmp/unit_tests_device_for_testers_release.zip - store_artifacts: path: /tmp/unit_tests_device_for_testers_release.zip destination: zips/unit_tests_device_for_testers_release.zip lint_device_release_check: executor: android/android resource_class: xlarge working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Lint check deviceRelease cmd: ":Corona-Warn-App:lintDeviceRelease -i" - store_artifacts: path: Corona-Warn-App/build/reports destination: reports lint_device_for_testers_release_check: executor: android/android resource_class: xlarge working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Lint check deviceForTestersRelease cmd: ":Corona-Warn-App:lintDeviceForTestersRelease -i" - store_artifacts: path: Corona-Warn-App/build/reports destination: reports ktlint_check: executor: android/android resource_class: medium working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Ktlint check cmd: ":Corona-Warn-App:ktlintCheck" - store_artifacts: path: Corona-Warn-App/build/reports destination: reports run_sonar: executor: android/android resource_class: xlarge working_directory: ~/project steps: - skip-for-external-pull-requests - attach_workspace: at: /home/circleci - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: JaCoCo report cmd: ":Corona-Warn-App:jacocoTestReportDeviceRelease -i" - scan-sonar quick_build_device_for_testers_signed: executor: android/android resource_class: xlarge working_directory: ~/project steps: - checkout - restore-gradle-cache - restore-android-build-cache - run: name: Download Keystore command: | curl --header "Authorization: token $keystore_download_token" --header "Accept: application/vnd.github.v3.raw" --remote-name --location "$keystore_download_url$keystore_download_filename" - run: name: Download Environment Properties command: | curl --header "Authorization: token $keystore_download_token" --header "Accept: application/vnd.github.v3.raw" --remote-name --location "$environments_download_url$env_prop_download_filename" - run: name: Decrypt Keystore command: openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 -in $keystore_download_filename -out $keystore_filename -k $keystore_encrypt_key - run: name: Prepare commit hash command: | commit_hash=$(echo $CIRCLE_SHA1 | cut -c1-5) echo $commit_hash echo "" >> "./gradle.properties" echo "commit_hash=$commit_hash" >> "./gradle.properties" - run: name: Prepare keystore properties for Signing command: | echo "" >> "./keystore.properties" echo "deviceForTestersRelease.storePath=../$keystore_filename" >> "./keystore.properties" echo "deviceForTestersRelease.storePassword=$keystore_password" >> "./keystore.properties" echo "deviceForTestersRelease.keyAlias=$key_alias" >> "./keystore.properties" echo "deviceForTestersRelease.keyPassword=$key_password" >> "./keystore.properties" - run-gradle-cmd: desc: Quick Build cmd: ":Corona-Warn-App:assembleDeviceForTestersRelease" - save-gradle-cache - save-android-build-cache - store_artifacts: path: Corona-Warn-App/build/outputs/apk destination: apk - store_artifacts: path: Corona-Warn-App/build/reports destination: reports - run: name: Send to T-System command: | fileName=$(find Corona-Warn-App/build/outputs/apk/deviceForTesters/release -name '*Corona-Warn-App*.apk') curl --location --request POST $tsystems_upload_url \ --header "Authorization: Bearer $tsystems_upload_bearer" \ --form "file=@${fileName}" \ instrumentation_tests_device: resource_class: xlarge docker: - image: circleci/android:api-28 steps: - skip-for-external-pull-requests - setup-google-cloud - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Build app APK cmd: ":Corona-Warn-App:assembleDeviceForTestersDebug" - run-gradle-cmd: desc: Build instrumentation test APK cmd: ":Corona-Warn-App:assembleDeviceForTestersDebugAndroidTest" - run: name: Setup Testlab environment command: | echo "export BUCKETDIR=\"`date "+%Y-%m-%d-%H:%M:%S:%3N"`-${RANDOM}\"" >> $BASH_ENV source $BASH_ENV echo "$BUCKETDIR is setup." - run: name: Test with Firebase Test Lab command: | echo "Using bucketdir $BUCKETDIR" sudo gcloud firebase test android run \ --type instrumentation \ --app Corona-Warn-App/build/outputs/apk/deviceForTesters/debug/*.apk \ --test Corona-Warn-App/build/outputs/apk/androidTest/deviceForTesters/debug/*.apk \ --results-dir ${BUCKETDIR} \ --results-bucket ${GOOGLE_PROJECT_ID}-circleci-android \ --environment-variables clearPackageData=true \ --test-targets "notAnnotation testhelpers.Screenshot" \ --timeout 20m \ --device-ids flame \ --os-version-ids 29 \ --locales de_DE \ --orientations portrait \ --no-record-video no_output_timeout: 30m - run: name: Create directory to store test results command: mkdir firebase-results when: always - run: name: Install gsutil dependency and copy test results data command: | sudo pip install -U crcmod sudo gsutil -m cp -R -U gs://${GOOGLE_PROJECT_ID}-circleci-android/${BUCKETDIR}/flame* firebase-results when: always - store_test_results: path: ./firebase-results/flame-29-de_DE-portrait - compress-path: input: ./firebase-results/flame-29-de_DE-portrait/*.xml output: /tmp/instrumentation_tests_device.zip - store_artifacts: path: /tmp/instrumentation_tests_device.zip destination: zips/instrumentation_tests_device.zip firebase_screenshots: resource_class: xlarge docker: - image: circleci/android:api-28 steps: - skip-for-external-pull-requests - setup-google-cloud - checkout - restore-gradle-cache - restore-android-build-cache - run-gradle-cmd: desc: Build APKs for screenshots cmd: > :Corona-Warn-App:assembleDebug :Corona-Warn-App:assembleAndroidTest - run: name: Setup Testlab environment command: | echo "export BUCKETDIR=\"`date "+%Y-%m-%d-%H:%M:%S:%3N"`-${RANDOM}\"" >> $BASH_ENV source $BASH_ENV echo "$BUCKETDIR is setup." - run: name: Test with Firebase Test Lab command: | echo "Using bucketdir $BUCKETDIR" sudo gcloud firebase test android run \ --type instrumentation \ --app Corona-Warn-App/build/outputs/apk/deviceForTesters/debug/*.apk \ --test Corona-Warn-App/build/outputs/apk/androidTest/deviceForTesters/debug/*.apk \ --results-dir ${BUCKETDIR} \ --results-bucket ${GOOGLE_PROJECT_ID}-circleci-android \ --environment-variables clearPackageData=true \ --test-targets "annotation testhelpers.Screenshot" \ --timeout 20m \ --device-ids flame \ --os-version-ids 29 \ --locales de_DE,en_US \ --orientations portrait \ --no-record-video no_output_timeout: 30m - run: name: Create directory to store test results command: mkdir firebase-screenshots when: always - run: name: Install gsutil dependency and copy test results data command: | sudo pip install -U crcmod sudo gsutil -m cp -R -U gs://${GOOGLE_PROJECT_ID}-circleci-android/${BUCKETDIR}/flame* firebase-screenshots sudo chown -R circleci:circleci firebase-screenshots when: always - store_test_results: path: ./firebase-screenshots - run: name: Clean up pulled bucket command: | rm -rf firebase-screenshots/*/artifacts/sdcard rm -rf firebase-screenshots/*/test_cases rm -f firebase-screenshots/*/instrumentation.results rm -f firebase-screenshots/*/logcat rm -f firebase-screenshots/*/test_result_1.xml mkdir /tmp/screenshots - compress-path: input: ./firebase-screenshots output: /tmp/screenshots/CWA-Android-Screenshots-${CIRCLE_SHA1}.zip - store_artifacts: path: /tmp/screenshots destination: zips/ ####################### # Workflow section # Job execution orders ####################### workflows: version: 2 check_buildtype_device: jobs: - detekt - lint_device_release_check - ktlint_check - quick_build_device_release_no_tests - unit_tests_device_release - run_sonar: requires: - unit_tests_device_release - instrumentation_tests_device: requires: - ktlint_check - detekt check_buildtype_device_for_testers: jobs: - detekt - lint_device_for_testers_release_check - ktlint_check - quick_build_device_for_testers_release_no_tests - unit_tests_device_for_testers_release signed_build: jobs: - quick_build_device_for_testers_signed: filters: tags: only: - /^v.*/ branches: ignore: /.*/ - firebase_screenshots: filters: tags: only: - /^v.*/ branches: ignore: /.*/