--- name: Android - Build and test on: pull_request: paths: - '**' - '!.github/workflows/**' - '.github/workflows/android-app.yml' - '!audits/**' - '!ci/**' - '!dist-assets/**' - '!docs/**' - '!graphics/**' - '!gui/**' - '!ios/**' - '!test/**' - '!scripts/**' - '!windows/**' - '!**/**.md' schedule: # At 06:20 UTC every day. # Notifications for scheduled workflows are sent to the user who last modified the cron # syntax in the workflow file. If you update this you must have notifications for # Github Actions enabled, so these don't go unnoticed. # https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/notifications-for-workflow-runs - cron: '20 6 * * *' workflow_dispatch: inputs: override_container_image: description: Override container image type: string required: false run_e2e_tests: description: Run e2e tests type: boolean required: false run_firebase_tests: description: Run firebase tests type: boolean required: false # Build if main is updated to ensure up-to-date caches are available push: branches: [main] jobs: prepare: name: Prepare runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Use custom container image if specified if: ${{ github.event.inputs.override_container_image != '' }} run: echo "inner_container_image=${{ github.event.inputs.override_container_image }}" >> $GITHUB_ENV - name: Use default container image and resolve digest if: ${{ github.event.inputs.override_container_image == '' }} run: | echo "inner_container_image=$(cat ./building/android-container-image.txt)" >> $GITHUB_ENV outputs: container_image: ${{ env.inner_container_image }} generate-relay-list: name: Generate relay list needs: prepare runs-on: ubuntu-latest container: image: ${{ needs.prepare.outputs.container_image }} steps: # Fix for HOME path overridden by GH runners when building in containers, see: # https://github.com/actions/runner/issues/863 - name: Fix HOME path run: echo "HOME=/root" >> $GITHUB_ENV - name: Get date id: get-date shell: bash run: | echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT - name: Cache uses: actions/cache@v3 id: cache-relay-list with: path: build/relays.json key: relay-list-${{ steps.get-date.outputs.date }} - name: Checkout repository if: steps.cache-relay-list.outputs.cache-hit != 'true' uses: actions/checkout@v3 - name: Generate if: steps.cache-relay-list.outputs.cache-hit != 'true' env: RUSTFLAGS: --deny warnings run: | mkdir -p build cargo run --bin relay_list > build/relays.json - name: Upload uses: actions/upload-artifact@v3 with: name: relay-list path: build/relays.json if-no-files-found: error retention-days: 7 build-native: name: Build native needs: prepare runs-on: ubuntu-latest container: image: "${{ needs.prepare.outputs.container_image }}" strategy: matrix: include: - arch: "x86_64" abi: "x86_64" target: "x86_64-linux-android" - arch: "i686" abi: "x86" target: "i686-linux-android" - arch: "aarch64" abi: "arm64-v8a" target: "aarch64-linux-android" - arch: "armv7" abi: "armeabi-v7a" target: "armv7-linux-androideabi" steps: # Fix for HOME path overridden by GH runners when building in containers, see: # https://github.com/actions/runner/issues/863 - name: Fix HOME path run: echo "HOME=/root" >> $GITHUB_ENV - name: Checkout repository uses: actions/checkout@v3 - name: Calculate native lib cache hash id: native-lib-cache-hash shell: bash run: | git config --global --add safe.directory $(pwd) non_android_hash="$(git grep --cached -l '' -- ':!android/' \ | xargs -d '\n' sha1sum \ | sha1sum \ | awk '{print $1}')" echo "native_lib_hash=$non_android_hash" >> $GITHUB_OUTPUT - name: Cache native libraries uses: actions/cache@v3 id: cache-native-libs env: cache_hash: ${{ steps.native-lib-cache-hash.outputs.native_lib_hash }} with: path: ./android/app/build/extraJni key: android-native-libs-${{ runner.os }}-${{ matrix.abi }}-${{ env.cache_hash }} - name: Build native libraries if: steps.cache-native-libs.outputs.cache-hit != 'true' env: RUSTFLAGS: --deny warnings BUILD_TYPE: debug run: | ARCHITECTURES="${{ matrix.abi }}" UNSTRIPPED_LIB_PATH="$CARGO_TARGET_DIR/${{ matrix.target }}/$BUILD_TYPE/libmullvad_jni.so" STRIPPED_LIB_PATH="./android/app/build/extraJni/${{ matrix.abi }}/libmullvad_jni.so" NDK_TOOLCHAIN_STRIP_TOOL="$NDK_TOOLCHAIN_DIR/llvm-strip" ./wireguard/build-wireguard-go.sh --android --no-docker cargo build --target ${{ matrix.target }} --verbose --package mullvad-jni --features api-override $NDK_TOOLCHAIN_STRIP_TOOL --strip-debug --strip-unneeded -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH" - name: Upload native libs uses: actions/upload-artifact@v3 with: name: native-libs path: android/app/build/extraJni if-no-files-found: error retention-days: 7 build-app: name: Build app and run unit tests needs: [prepare, build-native, generate-relay-list] runs-on: ubuntu-latest container: image: ${{ needs.prepare.outputs.container_image }} steps: # Fix for HOME path overridden by GH runners when building in containers, see: # https://github.com/actions/runner/issues/863 - name: Fix HOME path run: echo "HOME=/root" >> $GITHUB_ENV - name: Checkout repository uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: name: native-libs path: android/app/build/extraJni - uses: actions/download-artifact@v3 with: name: relay-list path: build - name: Build app uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: assembleOssProdDebug gradle-version: wrapper build-root-directory: android - name: Build stagemole app uses: burrunan/gradle-cache-action@v1 if: github.event_name == 'schedule' || github.event.inputs.run_firebase_tests == 'true' with: job-id: jdk17 arguments: assemblePlayStagemoleDebug gradle-version: wrapper build-root-directory: android - name: Run unit tests uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: | testDebugUnitTest -x :test:arch:testDebugUnitTest :app:testOssProdDebugUnitTest :service:testOssProdDebugUnitTest :lib:billing:testDebugUnitTest gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Run arch tests uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: :test:arch:test --rerun-tasks gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Run detekt uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: detekt gradle-version: wrapper build-root-directory: android execution-only-caches: true # Running the AGP lint here rather than in the separate lint workflow # (android-kotlin-format-check.yml) since it's easier to make use of the running container, # cache and previously ran tasks. - name: Run AGP lint uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: lint gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Assemble instrumented test apk (app) uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: assembleOssProdAndroidTest gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Assemble instrumented test apk (mockapi) uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: :test:mockapi:assemble gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Assemble instrumented test apk (e2e) uses: burrunan/gradle-cache-action@v1 with: job-id: jdk17 arguments: :test:e2e:assemble gradle-version: wrapper build-root-directory: android execution-only-caches: true - name: Upload apks uses: actions/upload-artifact@v3 with: name: apks path: | android/app/build/outputs/apk android/test/mockapi/build/outputs/apk android/test/e2e/build/outputs/apk if-no-files-found: error retention-days: 7 instrumented-tests: name: Run instrumented tests runs-on: [self-hosted, android-device] timeout-minutes: 30 needs: [build-app] strategy: fail-fast: false matrix: test-type: [app, mockapi] steps: - name: Checkout repository uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: name: apks path: android - name: Run instrumented test script shell: bash -ieo pipefail {0} env: AUTO_FETCH_TEST_HELPER_APKS: true TEST_TYPE: ${{ matrix.test-type }} BILLING_FLAVOR: oss INFRA_FLAVOR: prod run: | ./android/scripts/run-instrumented-tests.sh - name: Upload instrumentation report (${{ matrix.test-type }}) uses: actions/upload-artifact@v3 if: always() with: name: ${{ matrix.test-type }}-instrumentation-report path: /tmp/mullvad-${{ matrix.test-type }}-instrumentation-report if-no-files-found: ignore retention-days: 7 instrumented-e2e-tests: name: Run instrumented e2e tests runs-on: [self-hosted, android-device] if: github.event_name == 'schedule' || github.event.inputs.run_e2e_tests == 'true' timeout-minutes: 30 needs: [build-app] steps: - name: Checkout repository uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: name: apks path: android - name: Run instrumented test script shell: bash -ieo pipefail {0} env: AUTO_FETCH_TEST_HELPER_APKS: true TEST_TYPE: e2e BILLING_FLAVOR: oss INFRA_FLAVOR: prod VALID_TEST_ACCOUNT_TOKEN: ${{ secrets.ANDROID_PROD_TEST_ACCOUNT }} INVALID_TEST_ACCOUNT_TOKEN: '0000000000000000' run: | ./android/scripts/run-instrumented-tests.sh firebase-tests: name: Run firebase tests if: github.event_name == 'schedule' || github.event.inputs.run_firebase_tests == 'true' runs-on: ubuntu-latest timeout-minutes: 30 needs: [build-app] env: FIREBASE_ENVIRONMENT_VARIABLES: "\ clearPackageData=true,\ runnerBuilder=de.mannodermaus.junit5.AndroidJUnit5Builder,\ invalid_test_account_token=0000000000000000,\ partner_auth=${{ secrets.STAGEMOLE_PARTNER_AUTH }}" strategy: fail-fast: false matrix: arg-spec-file: [mockapi-oss.yml, e2e-play-stagemole.yml] steps: - name: Checkout repository uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: name: apks path: android - name: Run tests on Firebase Test Lab uses: asadmansr/Firebase-Test-Lab-Action@v1.0 env: SERVICE_ACCOUNT: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} with: arg-spec: | android/test/firebase/${{ matrix.arg-spec-file }}:default --environment-variables ${{ env.FIREBASE_ENVIRONMENT_VARIABLES }}