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: /.*/