secant-android-wallet/.github/workflows/pull-request.yml

530 lines
20 KiB
YAML

# 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
# FIREBASE_DEBUG_JSON_BASE64 - Optional JSON to enable Firebase (e.g. Crashlytics) for debug builds
# FIREBASE_RELEASE_JSON_BASE64 - Optional JSON to enable Firebase (e.g. Crashlytics) for release builds
# 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
# 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@55e685c48d84285a5b0418cd094606e199cca3b6
prime_cache:
needs: validate_gradle_wrapper
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
timeout-minutes: 1
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 12
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Android Lint
timeout-minutes: 15
env:
# Disable minify, since it makes lint run faster
ORG_GRADLE_PROJECT_IS_MINIFY_ENABLED: false
run: |
./gradlew :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_kotlin_modules:
needs: prime_cache
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
timeout-minutes: 1
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Test
timeout-minutes: 4
run: |
# Note that we explicitly check just the Kotlin modules, to avoid compiling the Android modules here
./gradlew :crash-lib:check :preference-api-lib:check :spackle-lib:check
- name: Collect Artifacts
if: ${{ always() }}
timeout-minutes: 1
env:
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_kotlin.zip', env.home) }}
run: |
mkdir ${ARTIFACTS_DIR_PATH}
zip -r ${RESULTS_ZIP_PATH} . -i *build/reports/*
- name: Upload Artifacts
if: ${{ always() }}
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
timeout-minutes: 1
with:
name: Test Kotlin modules results
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Build
timeout-minutes: 20
run: |
./gradlew assembleDebug assembleAndroidTest assembleZcashmainnetDebug assembleZcashtestnetDebug
- name: Authenticate to Google Cloud for Firebase Test Lab
id: auth_test_lab
uses: google-github-actions/auth@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d
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:
# Force blank suffix for screenshot tests
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
# 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_coverage:
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Build and test
timeout-minutes: 30
env:
# Force blank suffix for screenshot tests
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }}
# Temporarily disabled https://issuetracker.google.com/issues/266109833
ORG_GRADLE_PROJECT_IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED: false
run: |
./gradlew testDebugWithEmulatorWtf :ui-integration-test:testZcashmainnetDebugWithEmulatorWtf
- 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 libs with WTF results
path: ~/artifacts
test_android_modules_wtf_no_coverage:
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Build and test
timeout-minutes: 30
env:
# Force blank suffix for screenshot tests
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }}
run: |
./gradlew :app:testZcashmainnetDebugWithEmulatorWtf :ui-screenshot-test:testZcashmainnetDebugWithEmulatorWtf
- 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@83fd05a356d7e2593de66fc9913b3002723633cb
timeout-minutes: 1
with:
name: Test Android app with WTF results
path: ~/artifacts
# Performs a button mash test on the debug build of the app with strict mode enabled
test_robo_debug:
if: needs.check_firebase_secrets.outputs.has-secrets == 'true'
needs: [check_firebase_secrets]
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
id-token: write
steps:
- name: Checkout
timeout-minutes: 1
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Export Google Services JSON
env:
FIREBASE_DEBUG_JSON_BASE64: ${{ secrets.FIREBASE_DEBUG_JSON_BASE64 }}
FIREBASE_RELEASE_JSON_BASE64: ${{ secrets.FIREBASE_RELEASE_JSON_BASE64 }}
if: "${{ env.FIREBASE_DEBUG_JSON_BASE64 != '' && env.FIREBASE_RELEASE_JSON_BASE64 != '' }}"
shell: bash
run: |
mkdir -p app/src/debug/
mkdir -p app/src/release/
echo ${FIREBASE_DEBUG_JSON_BASE64} | base64 --decode > app/src/debug/google-services.json
echo ${FIREBASE_RELEASE_JSON_BASE64} | base64 --decode > app/src/release/google-services.json
- name: Build
timeout-minutes: 20
env:
ORG_GRADLE_PROJECT_IS_CRASH_ON_STRICT_MODE_VIOLATION: true
run: |
./gradlew :app:assembleDebug
- name: Authenticate to Google Cloud for Firebase Test Lab
id: auth_test_lab
uses: google-github-actions/auth@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d
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: Robo test
timeout-minutes: 20
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 }}
run: |
./gradlew :app:runFlankSanityConfigDebug
build:
needs: prime_cache
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
timeout-minutes: 1
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- name: Setup
id: setup
timeout-minutes: 5
uses: ./.github/actions/setup
- name: Export Google Services JSON
env:
FIREBASE_DEBUG_JSON_BASE64: ${{ secrets.FIREBASE_DEBUG_JSON_BASE64 }}
FIREBASE_RELEASE_JSON_BASE64: ${{ secrets.FIREBASE_RELEASE_JSON_BASE64 }}
if: "${{ env.FIREBASE_DEBUG_JSON_BASE64 != '' && env.FIREBASE_RELEASE_JSON_BASE64 != '' }}"
shell: bash
run: |
mkdir -p app/src/debug/
mkdir -p app/src/release/
echo ${FIREBASE_DEBUG_JSON_BASE64} | base64 --decode > app/src/debug/google-services.json
echo ${FIREBASE_RELEASE_JSON_BASE64} | base64 --decode > app/src/release/google-services.json
# 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: 20
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: |
./gradlew :app:assembleDebug :app:bundleRelease :app:packageZcashmainnetReleaseUniversalApk
- 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 app/build/outputs/apk/\*/\*.apk app/build/outputs/apk_from_bundle/\*/\*.apk app/build/outputs/bundle/\*/\*.aab
zip -r ${MAPPINGS_ZIP_PATH} . -i *app/build/outputs/mapping/*/mapping.txt
- name: Upload Artifacts
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
timeout-minutes: 2
with:
name: Binaries
path: ~/artifacts
# Performs a button mash test on the release build of the app
test_robo_release:
if: needs.check_firebase_secrets.outputs.has-secrets == 'true'
needs: [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@ac593985615ec2ede58e132d2e21d2b1cbd6127c
- 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@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d
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: Binaries
- name: Robo test
timeout-minutes: 20
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 :app:runFlankSanityConfigRelease