# Expected secrets # EMULATOR_WTF_API_KEY - Optional API key for emulator.wtf # FIREBASE_TEST_LAB_SERVICE_ACCOUNT - Email address of Firebase Test Lab service account # FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER - Workload identity provider to generate temporary service account key # Expected variables # FIREBASE_TEST_LAB_PROJECT - Firebase Test Lab project name name: Pull Request on: pull_request: paths-ignore: - '.github/ISSUE_TEMPLATE/*' - '.github/PULL_REQUEST_TEMPLATE.md' - 'LICENSE' - 'README.md' - 'docs/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: validate_gradle_wrapper: runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # Gradle Wrapper validation can be flaky # https://github.com/gradle/wrapper-validation-action/issues/40 - name: Gradle Wrapper Validation timeout-minutes: 1 uses: gradle/wrapper-validation-action@8d49e559aae34d3e0eb16cde532684bc9702762b prime_cache: runs-on: ubuntu-latest needs: validate_gradle_wrapper permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 40 uses: ./.github/actions/setup check_firebase_secrets: runs-on: ubuntu-latest outputs: has-secrets: ${{ steps.check_firebase_secrets.outputs.defined }} steps: - id: check_firebase_secrets env: FIREBASE_TEST_LAB_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} FIREBASE_TEST_LAB_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} if: "${{ env.FIREBASE_TEST_LAB_PROJECT != '' && env.FIREBASE_TEST_LAB_SERVICE_ACCOUNT != '' && env.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER != '' }}" run: echo "defined=true" >> $GITHUB_OUTPUT check_emulator_wtf_secrets: runs-on: ubuntu-latest outputs: has-secrets: ${{ steps.check_emulator_wtf_secrets.outputs.defined }} steps: - id: check_emulator_wtf_secrets env: EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }} if: "${{ env.EMULATOR_WTF_API_KEY != '' }}" run: echo "defined=true" >> $GITHUB_OUTPUT check_properties: needs: prime_cache runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Check properties timeout-minutes: 4 run: | ./gradlew checkProperties static_analysis_detekt: needs: prime_cache runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Detekt timeout-minutes: 4 run: | ./gradlew detektAll - name: Collect Artifacts timeout-minutes: 1 if: ${{ always() }} env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} REPORTS_ZIP_PATH: ${{ format('{0}/artifacts/static_analysis_detekt.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${REPORTS_ZIP_PATH} . -i build/reports/detekt/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Detekt static analysis results path: ~/artifacts static_analysis_ktlint: needs: prime_cache runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Ktlint timeout-minutes: 4 run: | ./gradlew ktlint - name: Collect Artifacts timeout-minutes: 1 if: ${{ always() }} env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} REPORTS_ZIP_PATH: ${{ format('{0}/artifacts/static_analysis_ktlint.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${REPORTS_ZIP_PATH} . -i build/reports/ktlint/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Ktlint static analysis results path: ~/artifacts static_analysis_android_lint: needs: prime_cache runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Android Lint timeout-minutes: 25 env: # Disable minify, since it makes lint run faster ORG_GRADLE_PROJECT_IS_MINIFY_APP_ENABLED: false run: | ./gradlew :sdk-lib:lintRelease :demo-app:lintZcashmainnetRelease - name: Collect Artifacts if: ${{ always() }} timeout-minutes: 1 env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} LINT_ZIP_PATH: ${{ format('{0}/artifacts/android_lint.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${LINT_ZIP_PATH} . -i \*build/reports/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Android Lint static analysis results path: ~/artifacts test_android_modules_unit: needs: [ prime_cache ] runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Build and test timeout-minutes: 25 run: | ./gradlew test - name: Collect Artifacts if: ${{ always() }} timeout-minutes: 1 env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} TEST_RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_results.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${TEST_RESULTS_ZIP_PATH} . -i \*/build/test-results/\* \*/build/reports/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Test Android modules with Unit Tests path: ~/artifacts # Emulator.wtf is preferred if it has an API key. test_android_modules_ftl: if: needs.check_firebase_secrets.outputs.has-secrets == 'true' && needs.check_emulator_wtf_secrets.outputs.has-secrets == 'false' needs: [prime_cache, check_firebase_secrets, check_emulator_wtf_secrets] runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Build timeout-minutes: 20 run: | ./gradlew assembleDebug assembleAndroidTest - name: Authenticate to Google Cloud for Firebase Test Lab id: auth_test_lab uses: google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033 with: create_credentials_file: true project_id: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} access_token_lifetime: '1200s' - name: Test timeout-minutes: 30 env: # This first environment variable is used by Flank, since the temporary token is missing the project name GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }} # Because Fulladle doesn't allow Test Orchestrator to be enabled/disabled for a specific submodule, it must be enabled for all modules ORG_GRADLE_PROJECT_IS_USE_TEST_ORCHESTRATOR: true run: | ./gradlew runFlank --parallel - name: Collect Artifacts if: ${{ always() }} timeout-minutes: 1 env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} TEST_RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_results.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${TEST_RESULTS_ZIP_PATH} . -i build/fladle/\* \*/build/outputs/androidTest-results/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Test Android modules with FTL results path: ~/artifacts test_android_modules_wtf: if: needs.check_emulator_wtf_secrets.outputs.has-secrets == 'true' needs: [ prime_cache, check_emulator_wtf_secrets ] runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Build and test timeout-minutes: 25 env: ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }} run: | ./gradlew testDebugWithEmulatorWtf - name: Collect Artifacts if: ${{ always() }} timeout-minutes: 1 env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} TEST_RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_results.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${TEST_RESULTS_ZIP_PATH} . -i \*/build/test-results/\* - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Test Android modules with WTF results path: ~/artifacts demo_app_release_build: needs: prime_cache runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup # A fake signing key to satisfy creating a "release" build - name: Export Signing Key env: SIGNING_KEY_PATH: ${{ format('{0}/release.jks', env.home) }} shell: bash run: | keytool -genkey -v -keystore $SIGNING_KEY_PATH -keypass android -storepass android -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 100000 -dname "CN=, OU=, O=Test, L=, S=, C=" -noprompt - name: Build timeout-minutes: 25 env: ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PATH: ${{ format('{0}/release.jks', env.home) }} ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PASSWORD: android ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: androiddebugkey ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: android run: | # Due to issues with the Rust integration, building the release APK requires running the task twice to # ensure the native libraries are bundled in the APK ./gradlew assembleRelease ./gradlew assembleRelease - name: Collect Artifacts timeout-minutes: 1 env: ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }} BINARIES_ZIP_PATH: ${{ format('{0}/artifacts/binaries.zip', env.home) }} MAPPINGS_ZIP_PATH: ${{ format('{0}/artifacts/mappings.zip', env.home) }} run: | mkdir ${ARTIFACTS_DIR_PATH} zip -r ${BINARIES_ZIP_PATH} . -i demo-app/build/outputs/apk/\*/release/\*.apk demo-app/build/outputs/bundle/\*/release/\*.aab zip -r ${MAPPINGS_ZIP_PATH} . -i demo-app/build/outputs/mapping/\*/mapping.txt - name: Upload Artifacts uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce timeout-minutes: 1 with: name: Demo app release binaries path: ~/artifacts # Performs a button mash test on the release build of the demo app test_robo_demo_app: if: needs.check_firebase_secrets.outputs.has-secrets == 'true' needs: [demo_app_release_build, check_firebase_secrets] runs-on: ubuntu-latest permissions: packages: read contents: read id-token: write steps: - name: Checkout timeout-minutes: 1 uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Setup id: setup timeout-minutes: 5 uses: ./.github/actions/setup - name: Authenticate to Google Cloud for Firebase Test Lab id: auth_test_lab uses: google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033 with: create_credentials_file: true project_id: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} access_token_lifetime: '900s' - name: Download a single artifact uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a with: name: Demo app release binaries - name: Robo test timeout-minutes: 15 env: # Path depends on `release_build` job, plus path of `Download a single artifact` step BINARIES_ZIP_PATH: binaries.zip # This first environment variable is used by Flank, since the temporary token is missing the project name GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }} run: | unzip ${BINARIES_ZIP_PATH} ./gradlew :demo-app:runFlankSanityConfig