protoncore_android/.gitlab-ci.yml

534 lines
17 KiB
YAML

include:
- project: 'agarroux/publish-github'
ref: master
file: '/jobs/release.gitlab-ci.yml'
- project: 'translations/generator'
ref: master
file: '/jobs/sync-crowdin.gitlab-ci.yml'
- project: 'translations/generator'
ref: master
file: '/jobs/commit-locales.gitlab-ci.yml'
- local: '/ci/templates-shared/appetize-integration.yml'
- local: '/ci/templates/git.gitlab-ci.yml'
- local: '/ci/templates/base-job.gitlab-ci.yml'
- local: '/ci/templates/tools.gitlab-ci.yml'
- project: 'proton/devops/atlas-deploy'
ref: main
file: '/scenarios/pipeline-env.yml'
default:
image: ${CI_REGISTRY}/android/shared/docker-android/oci:v2.0.0
tags:
- shared-small
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
variables:
# deploy to Atlas with prep job
ATLAS_DEPLOY_ENV: "true"
ATLAS_DEPLOY_PREP: "true"
- if: $CI_COMMIT_REF_NAME =~ /^release/
- if: $CI_COMMIT_REF_NAME == 'main'
stages:
- prepare
- bot
- deploy
- build
- test
- firebase
- publish
- review
- cleanup
variables:
# Use fastzip to improve cache times
FF_USE_FASTZIP: "true"
# Output upload and download progress every 5 seconds
TRANSFER_METER_FREQUENCY: "5s"
# Use no compression for artifacts
ARTIFACT_COMPRESSION_LEVEL: "fastest"
# Use low compression for caches
CACHE_COMPRESSION_LEVEL: "fast"
## pre stage #######################################################################################
prepare-environment:
tags:
- shared-small
extends: .script-job
stage: prepare
when: always
image: $CI_REGISTRY/proton/devops/atlas-cli
variables:
NAMESERVICE_URL: "https://nameservice.$ATLAS_BASE_DOMAIN/api/env"
REQUEST_DATA: "--header 'Content-Type:application/x-www-form-urlencoded' --data app=${CI_PROJECT_PATH} --data branch=${CI_COMMIT_REF_SLUG} --data tag=${CI_COMMIT_REF_SLUG}"
SENSITIVE_DIR: "test/quark/src/main/resources/sensitive"
script:
- echo PROXY_TOKEN="$(curl -o - https://proxy.$ATLAS_BASE_DOMAIN/token/get)" >> local.properties
- echo HOST="$DYNAMIC_DOMAIN" >> local.properties
- mkdir -p $SENSITIVE_DIR
- cat $TEST_USERS > $SENSITIVE_DIR/users.json
- cat $TEST_USERS_VPN_USERNAME > $SENSITIVE_DIR/users-vpn-username.json
- cat $TEST_INTERNAL_API_V4 > $SENSITIVE_DIR/internal_apis.json
rules:
- if: $ATLAS_DEPLOY_ENV == "true"
artifacts:
paths:
- $SENSITIVE_DIR
- local.properties
expire_in: never
detekt-analyze:
extends: .gradle-job
stage: prepare
when: always
interruptible: true
script:
- ./gradlew multiModuleDetekt
artifacts:
expire_in: 1 month
reports:
codequality: config/detekt/reports/mergedReport.json
## bot stage #######################################################################################
i18n-sync-crowdin:
extends: .i18n-sync-crowdin-common
variables:
I18N_SYNC_CROWDIN_PROJECT: 'android-core'
I18N_SYNC_BRANCH: 'main'
I18N_FILTER_OUT_ITEMS: 'coreexample'
i18n-commit-locales:
extends: .i18n-commit-locales-shared
variables:
I18N_COMMIT_CROWDIN_PROJECT: 'android-core'
I18N_COMMIT_BRANCH_PUSH: 'main'
I18N_COMMIT_BRANCH_ALLOWED: 'main'
grafana-commit-dashboards:
extends: .gradle-job
stage: prepare
allow_failure: true
before_script:
- source /load-env.sh
script:
- ./ci/script/pushDashboards.sh
rules:
- if: $DASHBOARDS_SCHEDULES_FILTER == "commit-dashboards"
## build stage #####################################################################################
assemble:
extends: .gradle-job
stage: build
needs:
- detekt-analyze
- job: prepare-environment
artifacts: true
interruptible: true
variables:
# Build the base apk
CORE_APP: :coreexample:assembleLocalPropertiesDebug
# Build UI tests for CoreExample
CORE_APP_TESTS: :coreexample:assembleLocalPropertiesDebugAndroidTest
# Mock variant and mock UI tests
CORE_APP_MOCK: :coreexample:assembleMockDebug :coreexample:assembleMockDebugAndroidTest
# Hilt tests
HILT_TESTS: :coreexample:coreexample-hilt-tests:assembleLocalPropertiesDebug
# Build androidTest apks for several projects
CORE_LIBS_TESTS: :user:user-data:assembleDebugAndroidTest :key:key-data:assembleDebugAndroidTest :crypto:crypto-android:assembleDebugAndroidTest :key-transparency:key-transparency-data:assembleDebugAndroidTest
script:
- ./gradlew $CORE_APP $CORE_APP_MOCK $CORE_APP_TESTS $CORE_LIBS_TESTS $HILT_TESTS
artifacts:
paths:
- coreexample/build/outputs/
- coreexample/hilt-tests/build/outputs/apk/
- user/data/build/outputs/apk/
- key/data/build/outputs/apk/
- crypto/android/build/outputs/apk/
- key-transparency/data/build/outputs/apk/
assemble:configurator:
extends: .gradle-job
stage: build
needs: [ ]
when: manual
allow_failure: true
interruptible: true
script:
- ./gradlew :configuration:configuration-configurator:assembleDebug
artifacts:
paths:
- configuration/configurator/build/outputs/apk/
## test stage ######################################################################################
unit-tests-and-coverage-report:
extends: .gradle-job
needs:
- assemble
stage: test
script:
- ./gradlew -Pci --console=plain :coreexample:testDevDebugUnitTest coberturaXmlReport globalLineCoverage :coverage:koverHtmlReport -x :coverage:jacocoToCobertura
- ./gradlew -Pci --console=plain koverVerify
- ./gradlew -Pci --console=plain verifyPaparazziDebug
coverage: /TotalLineCoverage.*?(\d{1,3}\.\d{0,2})%/
interruptible: true
artifacts:
when: always
expire_in: 1 week
paths:
- '**/build/reports/kover/cobertura*.xml'
- './coverage/build/reports/kover/html/'
reports:
junit: ./**/test-results/*/TEST-*.xml
coverage_report:
coverage_format: cobertura
path: '**/build/reports/kover/cobertura*.xml'
start-review:
needs:
- assemble
before_script:
- source /load-env.sh
- export REVIEW_APP_ARTIFACT_PATH="coreexample/build/outputs/apk/localProperties/debug/coreexample-localProperties-debug.apk"
extends: .startReview
stage: test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
binary-compatibility-check:
extends: .gradle-job
stage: test
interruptible: true
needs:
- assemble
script:
- ./gradlew apiCheck
.client-integration-tests:
stage: test
inherit:
variables: false
variables:
CORE_COMMIT_SHA: $CI_COMMIT_SHA
ORG_GRADLE_PROJECT_useCoreGitSubmodule: "true"
calendar-integration-tests:
extends: .client-integration-tests
when: manual
variables:
CALENDAR_INTEGRATION_REF: develop
trigger:
project: android/calendar/proton-calendar-android
branch: $CALENDAR_INTEGRATION_REF
mail-v6-integration-tests:
extends: .client-integration-tests
when: manual
variables:
MAIL_V6_INTEGRATION_REF: master
trigger:
project: android/mail/proton-mail
branch: $MAIL_V6_INTEGRATION_REF
mail-v5-integration-tests:
extends: .client-integration-tests
when: manual
variables:
MAIL_INTEGRATION_REF: develop
trigger:
project: android/mail/proton-mail-android
branch: $MAIL_INTEGRATION_REF
vpn-integration-tests:
extends: .client-integration-tests
when: manual
variables:
VPN_INTEGRATION_REF: development
trigger:
project: ProtonVPN/android/android-app-new
branch: $VPN_INTEGRATION_REF
drive-integration-tests:
extends: .client-integration-tests
when: manual
variables:
DRIVE_INTEGRATION_REF: develop
trigger:
project: android/drive/proton-drive
branch: $DRIVE_INTEGRATION_REF
pass-integration-tests:
extends: .client-integration-tests
when: manual
variables:
PASS_INTEGRATION_REF: main
trigger:
project: android/pass/ProtonPass
branch: $PASS_INTEGRATION_REF
## firebase stage ##################################################################################
.firebase-tests-common:
stage: firebase
when: always
variables:
APP_APK: "coreexample/build/outputs/apk/localProperties/debug/coreexample-localProperties-debug.apk"
TEST_APK: "coreexample/build/outputs/apk/androidTest/localProperties/debug/coreexample-localProperties-debug-androidTest.apk"
RESULTS_DIR: "${CI_COMMIT_REF_SLUG}/${CI_COMMIT_SHORT_SHA}/${CI_JOB_NAME}"
DEVICE_MODEL: "Pixel2,version=28"
DEVICE_MODEL_RESULTS_DIR: "Pixel2-28-en-portrait"
EXTRA_OPTIONS: ""
FLAKY_TEST_RERUN: "0"
tags:
- shared-medium
allow_failure: false
script:
- gcloud config set project $CLOUD_PROJECT_ID_CORE
- gcloud auth activate-service-account --key-file $GOOGLE_SERVICE_ACCOUNT
- gcloud firebase test android run
--app ${APP_APK}
--test ${TEST_APK}
--device model=${DEVICE_MODEL}
--test-targets "$TEST_TARGET"
--timeout 30m
--results-dir=$RESULTS_DIR
--num-flaky-test-attempts=$FLAKY_TEST_RERUN
--environment-variables listener=me.proton.core.test.android.ToastingRunListener
--client-details=matrixLabel="$CI_JOB_NAME $CI_PIPELINE_URL"
${EXTRA_OPTIONS}
after_script:
- mkdir -p screenshots
- mkdir -p logs
- gsutil cp $FIREBASE_BUCKET_URL/$RESULTS_DIR/$DEVICE_MODEL_RESULTS_DIR/logcat logcat.txt
- gsutil cp $FIREBASE_BUCKET_URL/$RESULTS_DIR/$DEVICE_MODEL_RESULTS_DIR/video.mp4 video.mp4
- gsutil cp $FIREBASE_BUCKET_URL/$RESULTS_DIR/$DEVICE_MODEL_RESULTS_DIR/test_result_1.xml test_result_1.xml
- gsutil -m cp -r $FIREBASE_BUCKET_URL/$RESULTS_DIR/$DEVICE_MODEL_RESULTS_DIR/test_cases ./logs
- gsutil -m cp -r $FIREBASE_BUCKET_URL/$RESULTS_DIR/$DEVICE_MODEL_RESULTS_DIR/artifacts/sdcard/screenshots ./screenshots 2> /dev/null || true
- python3 coreexample/ci/process_report.py test_result_1.xml test_result.xml
needs:
- assemble
- deploy:review
artifacts:
when: always
reports:
junit: test_result.xml
paths:
- test_result.xml
- screenshots
- logcat.txt
- video.mp4
- logs
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
uitest:signup:
allow_failure: true
variables:
TEST_TARGET: "package me.proton.core.test.android.uitests.tests.medium.auth.signup"
extends: .firebase-tests-common
uitest:login-and-hv:
allow_failure: true
variables:
TEST_TARGET: "package me.proton.core.test.android.uitests.tests.medium.auth.login,package me.proton.core.test.android.uitests.tests.medium.humanverification"
extends: .firebase-tests-common
uitest:plans-and-payments:
allow_failure: true
variables:
TEST_TARGET: "package me.proton.core.test.android.uitests.tests.medium.plans,package me.proton.core.test.android.uitests.tests.medium.payments"
extends: .firebase-tests-common
uitest:usersettings:
allow_failure: true
variables:
TEST_TARGET: "package me.proton.core.test.android.uitests.tests.medium.usersettings"
extends: .firebase-tests-common
uitest:mocked-tests:
variables:
APP_APK: "coreexample/build/outputs/apk/mock/debug/coreexample-mock-debug.apk"
TEST_APK: "coreexample/build/outputs/apk/androidTest/mock/debug/coreexample-mock-debug-androidTest.apk"
TEST_TARGET: "package me.proton.core.test.android.mockuitests"
FLAKY_TEST_RERUN: "1"
DEVICE_MODEL: "Pixel2.arm,version=28"
DEVICE_MODEL_RESULTS_DIR: "Pixel2.arm-28-en-portrait"
EXTRA_OPTIONS: "--use-orchestrator --environment-variables clearPackageData=true"
extends: .firebase-tests-common
uitest:hilt-tests:
allow_failure: true
variables:
TEST_APK: "coreexample/hilt-tests/build/outputs/apk/localProperties/debug/coreexample-hilt-tests-localProperties-debug.apk"
TEST_TARGET: "package me.proton.android.core.coreexample.hilttests"
DEVICE_MODEL: "Pixel2.arm,version=28"
DEVICE_MODEL_RESULTS_DIR: "Pixel2.arm-28-en-portrait"
EXTRA_OPTIONS: "--use-orchestrator --environment-variables clearPackageData=true"
extends: .firebase-tests-common
androidTest:user-data:
variables:
TEST_APK: "user/data/build/outputs/apk/androidTest/debug/user-data-debug-androidTest.apk"
TEST_TARGET: "package me.proton.core.user.data"
extends: .firebase-tests-common
androidTest:key-data:
variables:
TEST_APK: "key/data/build/outputs/apk/androidTest/debug/key-data-debug-androidTest.apk"
TEST_TARGET: "package me.proton.core.key.domain" # Domain entities are Android Tested in data
extends: .firebase-tests-common
androidTest:crypto-android:
variables:
TEST_APK: "crypto/android/build/outputs/apk/androidTest/debug/crypto-android-debug-androidTest.apk"
TEST_TARGET: "package me.proton.core.crypto.android"
extends: .firebase-tests-common
androidTest:key-transparency-data:
variables:
TEST_APK: "key-transparency/data/build/outputs/apk/androidTest/debug/key-transparency-data-debug-androidTest.apk"
TEST_TARGET: "package me.proton.core.keytransparency.data"
DEVICE_MODEL: "Pixel2.arm,version=28"
DEVICE_MODEL_RESULTS_DIR: "Pixel2.arm-28-en-portrait"
extends: .firebase-tests-common
androidTest:coreexample-migration:
variables:
TEST_TARGET: "package me.proton.core.test.android.dbtests"
extends: .firebase-tests-common
## publish stage ###################################################################################
pages:
extends: .script-job
needs:
- unit-tests-and-coverage-report
stage: publish
script:
- mkdir -p ./public/coverage
- mv ./coverage/build/reports/kover/html/* ./public/coverage
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == 'main'
trigger-new-libs-release:
extends: .script-job
stage: publish
when: manual
resource_group: release/libs
tags:
- shared-small
variables:
NEXT_VERSION: '' # If empty, the version will be automatically calculated.
before_script:
- source /load-env.sh
- !reference [.download-tools, script]
script:
- ./ci/script/prepareRelease.sh
rules:
- if: $CI_COMMIT_BRANCH == 'main'
release-publish-github:
stage: publish
tags:
- shared-small
variables:
RELEASE_SYNC_PUBLIC_URL: git@github.com:ProtonMail/protoncore_android.git
RELEASE_SYNC_TO_BRANCH: 'main'
RELEASE_SYNC_FROM_BRANCH: 'main'
extends: .release-sync-commit-shared
publish-libs:
extends: .gradle-job
stage: publish
resource_group: release/libs
variables:
ORG_GRADLE_PROJECT_mavenCentralUsername: $MAVEN_USER
ORG_GRADLE_PROJECT_mavenCentralPassword: $MAVEN_PASSWORD
ORG_GRADLE_PROJECT_signingInMemoryKey: $MAVEN_SIGNING_KEY
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: $MAVEN_SIGNING_KEY_PASSWORD
ORG_GRADLE_PROJECT_androidDevChannelWebhook: $ANDROID_DEVS_CHANNEL_WEBHOOK
script:
- ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
- ./gradlew notifyNewRelease
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/libs\/.*/ # stable release with notification
tag-libs-release:
extends: .gradle-job
stage: publish
resource_group: release/libs
needs:
- job: publish-libs
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/libs\/.*/
before_script:
- !reference [ .git, before_script ]
script:
- ./gradlew tagRelease
publish-gradle-plugins:
extends: .gradle-job
stage: publish
variables:
ORG_GRADLE_PROJECT_mavenCentralUsername: $MAVEN_USER
ORG_GRADLE_PROJECT_mavenCentralPassword: $MAVEN_PASSWORD
ORG_GRADLE_PROJECT_signingInMemoryKey: $MAVEN_SIGNING_KEY
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: $MAVEN_SIGNING_KEY_PASSWORD
script:
- ./gradlew -p plugins publishToSonatype closeAndReleaseSonatypeStagingRepository
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/gradle-plugins\/.*/ # stable release with notification
tag-gradle-plugins-release:
extends: .gradle-job
stage: publish
needs:
- job: publish-gradle-plugins
before_script:
- !reference [ .git, before_script ]
script:
- ./gradlew -p plugins tagRelease
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/gradle-plugins\/.*/
## review stage ######################################################################################
danger-review:
tags:
- shared-medium
extends: .ruby-job
stage: review
when: always
needs:
- unit-tests-and-coverage-report
script:
- !reference [.download-tools, script]
- bundle exec danger --fail-on-errors=false
allow_failure: true
variables:
DANGER_GITLAB_API_TOKEN: $DANGER_GITLAB_API_TOKEN
DANGER_COBERTURA_GIT_URL: https://${GIT_CI_USERNAME}:${PRIVATE_TOKEN_GITLAB_API_PROTON_CI}@${CI_SERVER_HOST}/proton/devops/quality/danger-cobertura.git
DANGER_RANDOM_REVIEWERS_GIT_URL: https://${GIT_CI_USERNAME}:${PRIVATE_TOKEN_GITLAB_API_PROTON_CI}@${CI_SERVER_HOST}/proton/devops/quality/danger-random-reviewers.git
interruptible: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# .startReview requires 'stopReview' job. See GitLab merged YAML.
stopReview:
extends: .stopReview
stage: cleanup
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true