380 lines
16 KiB
YAML
380 lines
16 KiB
YAML
name: Firebase Test Lab
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'android/**'
|
|
- '.github/workflows/firebase-test-lab.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- 'android/**'
|
|
- '.github/workflows/firebase-test-lab.yml'
|
|
# Allow manual trigger for release verification
|
|
workflow_dispatch:
|
|
inputs:
|
|
build_type:
|
|
description: 'Build type to test'
|
|
required: true
|
|
default: 'release'
|
|
type: choice
|
|
options:
|
|
- release
|
|
- debug
|
|
skip_robo:
|
|
description: 'Skip Robo tests'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
skip_instrumentation:
|
|
description: 'Skip instrumentation tests'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# ============================================================================
|
|
# Build Job: Compile the Android app and test APK
|
|
# ============================================================================
|
|
build:
|
|
name: Build APKs
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up JDK 17
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
java-version: '17'
|
|
distribution: 'temurin'
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v4
|
|
with:
|
|
gradle-version: wrapper
|
|
|
|
- name: Cache Gradle dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
android/.gradle
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle.kts', 'android/gradle/libs.versions.toml') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-gradle-
|
|
|
|
- name: Build release APK
|
|
run: |
|
|
cd android
|
|
./gradlew :app:assembleProdRelease :app:assembleProdDebugAndroidTest --no-daemon
|
|
|
|
- name: Upload app APK
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: app-release-apk
|
|
path: android/app/build/outputs/apk/prod/release/*.apk
|
|
retention-days: 7
|
|
|
|
- name: Upload test APK
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: app-test-apk
|
|
path: android/app/build/outputs/apk/androidTest/prod/debug/*.apk
|
|
retention-days: 7
|
|
|
|
- name: Upload AAB (for Robo tests)
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: app-release-aab
|
|
path: android/app/build/outputs/bundle/prodRelease/*.aab
|
|
retention-days: 7
|
|
|
|
# ============================================================================
|
|
# Robo Tests Job: Crash/ANR detection via autonomous crawl
|
|
# ============================================================================
|
|
robo-tests:
|
|
name: Robo Tests
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Authenticate to Google Cloud
|
|
uses: google-github-actions/auth@v2
|
|
with:
|
|
credentials_json: ${{ secrets.GCP_SA_KEY_TEST_LAB }}
|
|
|
|
- name: Set up Cloud SDK
|
|
uses: google-github-actions/setup-gcloud@v2
|
|
with:
|
|
project_id: ${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}
|
|
|
|
- name: Download AAB
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: app-release-aab
|
|
path: build/
|
|
- name: Download APK (fallback)
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: app-release-apk
|
|
path: build/
|
|
|
|
- name: Run Robo tests
|
|
id: robo
|
|
run: |
|
|
# Check which file type is available (prefer AAB)
|
|
AAB_FILE=$(find build -name "*.aab" -type f 2>/dev/null | head -1)
|
|
APK_FILE=$(find build -name "*prod-release.apk" -type f 2>/dev/null | head -1)
|
|
|
|
SCRIPT_DIR="android/firebase-test-lab"
|
|
|
|
if [ -n "$AAB_FILE" ]; then
|
|
echo "Using AAB: $AAB_FILE"
|
|
gcloud firebase test android run \
|
|
--type robo \
|
|
--project "${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}" \
|
|
--app-package "com.kordant.android" \
|
|
--aab "$AAB_FILE" \
|
|
--robo-script "$SCRIPT_DIR/robo_script.json" \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=landscape \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=landscape \
|
|
--timeout 60m \
|
|
--max-crawl-time 600 \
|
|
--record-video \
|
|
--performance-metrics \
|
|
--results-history-name "Kordant Android Robo CI" \
|
|
--fail-fast \
|
|
|| ROBO_EXIT=$?
|
|
elif [ -n "$APK_FILE" ]; then
|
|
echo "Using APK: $APK_FILE"
|
|
gcloud firebase test android run \
|
|
--type robo \
|
|
--project "${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}" \
|
|
--app "$APK_FILE" \
|
|
--robo-script "$SCRIPT_DIR/robo_script.json" \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=landscape \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=landscape \
|
|
--timeout 60m \
|
|
--max-crawl-time 600 \
|
|
--record-video \
|
|
--performance-metrics \
|
|
--results-history-name "Kordant Android Robo CI" \
|
|
--fail-fast \
|
|
|| ROBO_EXIT=$?
|
|
else
|
|
echo "No APK or AAB found!"
|
|
exit 1
|
|
fi
|
|
|
|
echo "ROBO_EXIT_CODE=${ROBO_EXIT:-0}" >> $GITHUB_OUTPUT
|
|
|
|
- name: Upload Robo test results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: robo-test-results
|
|
path: |
|
|
android/firebase-test-lab/robo_script.json
|
|
build/*.aab
|
|
build/*prod-release*.apk
|
|
retention-days: 14
|
|
|
|
- name: Mark build as failed if Robo tests failed
|
|
if: steps.robo.outputs.ROBO_EXIT_CODE != '0'
|
|
run: |
|
|
echo "❌ Robo tests failed with exit code ${{ steps.robo.outputs.ROBO_EXIT_CODE }}"
|
|
echo "Review results in Firebase Console"
|
|
exit 1
|
|
|
|
# ============================================================================
|
|
# Instrumentation Tests Job: UI tests with assertions
|
|
# ============================================================================
|
|
instrumentation-tests:
|
|
name: Instrumentation Tests
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Authenticate to Google Cloud
|
|
uses: google-github-actions/auth@v2
|
|
with:
|
|
credentials_json: ${{ secrets.GCP_SA_KEY_TEST_LAB }}
|
|
|
|
- name: Set up Cloud SDK
|
|
uses: google-github-actions/setup-gcloud@v2
|
|
with:
|
|
project_id: ${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}
|
|
|
|
- name: Download APKs
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: app-release-apk
|
|
path: build/apk/
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: app-test-apk
|
|
path: build/apk/
|
|
|
|
- name: Run Instrumentation tests
|
|
id: instrumentation
|
|
run: |
|
|
APP_APK=$(find build/apk -name "*prod-release.apk" -type f 2>/dev/null | head -1)
|
|
TEST_APK=$(find build/apk -name "*androidTest*.apk" -type f 2>/dev/null | head -1)
|
|
|
|
if [ -z "$APP_APK" ] || [ -z "$TEST_APK" ]; then
|
|
echo "Error: Could not find APK files."
|
|
echo "App APK: ${APP_APK:-not found}"
|
|
echo "Test APK: ${TEST_APK:-not found}"
|
|
exit 1
|
|
fi
|
|
|
|
echo "App APK: $APP_APK"
|
|
echo "Test APK: $TEST_APK"
|
|
|
|
gcloud firebase test android run \
|
|
--type instrumentation \
|
|
--project "${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}" \
|
|
--app "$APP_APK" \
|
|
--test "$TEST_APK" \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=en_US,orientation=landscape \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel6,version=33,locale=es_ES,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=en_US,orientation=landscape \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=portrait \
|
|
--device model=Pixel4,version=30,locale=es_ES,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=en_US,orientation=landscape \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=portrait \
|
|
--device model=GalaxyS21,version=31,locale=es_ES,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=en_US,orientation=landscape \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=portrait \
|
|
--device model=RedmiNote8,version=29,locale=es_ES,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=en_US,orientation=landscape \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=portrait \
|
|
--device model=AquestM2,version=28,locale=es_ES,orientation=landscape \
|
|
--timeout 60m \
|
|
--num-flaky-test-attempts 2 \
|
|
--record-video \
|
|
--performance-metrics \
|
|
--results-history-name "Kordant Android Instrumentation CI" \
|
|
--fail-fast \
|
|
|| INSTR_EXIT=$?
|
|
|
|
echo "INSTR_EXIT_CODE=${INSTR_EXIT:-0}" >> $GITHUB_OUTPUT
|
|
|
|
- name: Upload instrumentation test results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: instrumentation-test-results
|
|
path: build/apk/
|
|
retention-days: 14
|
|
|
|
- name: Mark build as failed if instrumentation tests failed
|
|
if: steps.instrumentation.outputs.INSTR_EXIT_CODE != '0'
|
|
run: |
|
|
echo "❌ Instrumentation tests failed with exit code ${{ steps.instrumentation.outputs.INSTR_EXIT_CODE }}"
|
|
echo "Review results in Firebase Console"
|
|
exit 1
|
|
|
|
# ============================================================================
|
|
# Summary Job: Collect all test results
|
|
# ============================================================================
|
|
test-summary:
|
|
name: Test Summary
|
|
needs: [robo-tests, instrumentation-tests]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Check test results
|
|
run: |
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📊 Firebase Test Lab - CI Results Summary"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "View detailed results in Firebase Console:"
|
|
echo " https://console.firebase.google.com/project/${{ secrets.FIREBASE_PROJECT_ID || 'kordant-android' }}/testlab"
|
|
echo ""
|
|
echo "Devices tested:"
|
|
echo " ✅ Pixel 6 (API 33) - primary target"
|
|
echo " ✅ Pixel 4 (API 30) - older device"
|
|
echo " ✅ Galaxy S21 (API 31) - Samsung"
|
|
echo " ✅ Redmi Note 8 (API 29) - Xiaomi"
|
|
echo " ✅ Aquest M2 (API 28) - low-end device"
|
|
echo ""
|
|
echo "Orientations: portrait, landscape"
|
|
echo "Locales: en_US, es_ES"
|
|
echo ""
|
|
|
|
- name: Send notification on failure
|
|
if: failure()
|
|
run: |
|
|
echo "::warning::Firebase Test Lab tests failed. Check the Firebase Console for details."
|
|
|
|
- name: Determine overall status
|
|
run: |
|
|
if [ "${{ needs.robo-tests.result }}" = "failure" ] || [ "${{ needs.instrumentation-tests.result }}" = "failure" ]; then
|
|
echo "❌ Firebase Test Lab: FAILED"
|
|
exit 1
|
|
else
|
|
echo "✅ Firebase Test Lab: PASSED"
|
|
fi
|