# 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
