feat: add RiveLogger with configurable JS handler and deprecation warnings #920
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| merge_group: | |
| types: | |
| - checks_requested | |
| jobs: | |
| lint-swift: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: SwiftLint | |
| uses: norio-nomura/action-swiftlint@3.2.1 | |
| with: | |
| args: --config .swiftlint.yml | |
| lint-kotlin: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: ktlint | |
| uses: ScaCap/action-ktlint@master | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| reporter: github-pr-review | |
| ktlint_version: "1.5.0" | |
| fail_on_error: true | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Lint files | |
| run: yarn lint | |
| - name: Validate Nitrogen Generated Code | |
| run: | | |
| yarn nitrogen | |
| if ! git diff --exit-code -I 'Copyright © [0-9]* Marc Rousavy @ Margelo' nitrogen/generated/; then | |
| echo "Error: nitrogen/generated/ is out of date. Please run 'yarn nitrogen' and commit the changes." | |
| exit 1 | |
| fi | |
| - name: Typecheck files | |
| run: yarn typecheck | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Run unit tests | |
| run: yarn test --maxWorkers=2 --coverage | |
| build-library: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Build package | |
| run: yarn prepare | |
| build-android: | |
| runs-on: ubuntu-latest | |
| env: | |
| TURBO_CACHE_DIR: .turbo/android | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Cache turborepo for Android | |
| id: turbo-cache-android | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ env.TURBO_CACHE_DIR }} | |
| key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock', 'android/**', 'nitrogen/generated/android/**', 'src/specs/*.nitro.ts') }} | |
| restore-keys: | | |
| ${{ runner.os }}-turborepo-android- | |
| - name: Check turborepo cache for Android | |
| run: | | |
| TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status") | |
| if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then | |
| echo "turbo_cache_hit=1" >> $GITHUB_ENV | |
| fi | |
| - name: Install JDK | |
| if: env.turbo_cache_hit != 1 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - name: Finalize Android SDK | |
| if: env.turbo_cache_hit != 1 | |
| run: | | |
| /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" | |
| - name: Cache Gradle | |
| if: env.turbo_cache_hit != 1 | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/wrapper | |
| ~/.gradle/caches | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Build example for Android | |
| env: | |
| JAVA_OPTS: "-XX:MaxHeapSize=6g" | |
| TURBO_FORCE: ${{ steps.turbo-cache-android.outputs.cache-hit != 'true' }} | |
| run: | | |
| yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" | |
| build-ios: | |
| runs-on: macos-latest | |
| env: | |
| XCODE_VERSION: 16.4 | |
| TURBO_CACHE_DIR: .turbo/ios | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Cache turborepo for iOS | |
| id: turbo-cache-ios | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ env.TURBO_CACHE_DIR }} | |
| key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock', 'ios/**', 'nitrogen/generated/ios/**', '*.podspec', 'src/specs/*.nitro.ts') }} | |
| restore-keys: | | |
| ${{ runner.os }}-turborepo-ios- | |
| - name: Check turborepo cache for iOS | |
| run: | | |
| TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status") | |
| if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then | |
| echo "turbo_cache_hit=1" >> $GITHUB_ENV | |
| fi | |
| - name: Use appropriate Xcode version | |
| if: env.turbo_cache_hit != 1 | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Restore cocoapods | |
| if: env.turbo_cache_hit != 1 | |
| id: cocoapods-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: | | |
| **/ios/Pods | |
| key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cocoapods- | |
| - name: Install cocoapods | |
| if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd example | |
| bundle install | |
| bundle exec pod install --project-directory=ios | |
| - name: Cache cocoapods | |
| if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: | | |
| **/ios/Pods | |
| key: ${{ steps.cocoapods-cache.outputs.cache-key }} | |
| - name: Build example for iOS | |
| env: | |
| TURBO_FORCE: ${{ steps.turbo-cache-ios.outputs.cache-hit != 'true' }} | |
| run: | | |
| yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" | |
| test-harness-ios: | |
| runs-on: macos-latest | |
| timeout-minutes: 60 | |
| env: | |
| XCODE_VERSION: 16.4 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Use appropriate Xcode version | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Restore cocoapods | |
| id: cocoapods-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: | | |
| **/ios/Pods | |
| key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cocoapods- | |
| - name: Install cocoapods | |
| if: steps.cocoapods-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd example | |
| bundle install | |
| bundle exec pod install --project-directory=ios | |
| - name: Restore iOS build cache | |
| id: ios-build-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: example/ios/build | |
| key: ${{ runner.os }}-ios-build-${{ env.XCODE_VERSION }}-${{ hashFiles('yarn.lock', 'ios/**', 'nitrogen/generated/ios/**', '*.podspec', 'example/ios/Podfile.lock', 'example/ios/RiveExample/**') }} | |
| restore-keys: | | |
| ${{ runner.os }}-ios-build-${{ env.XCODE_VERSION }}- | |
| - name: Build iOS app | |
| if: steps.ios-build-cache.outputs.cache-hit != 'true' | |
| working-directory: example/ios | |
| run: | | |
| set -o pipefail && xcodebuild \ | |
| -derivedDataPath build \ | |
| -workspace RiveExample.xcworkspace \ | |
| -scheme RiveExample \ | |
| -sdk iphonesimulator \ | |
| -configuration Debug \ | |
| build \ | |
| CODE_SIGNING_ALLOWED=NO | |
| - name: Save iOS build cache | |
| if: steps.ios-build-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: example/ios/build | |
| key: ${{ steps.ios-build-cache.outputs.cache-primary-key }} | |
| - name: Boot iOS Simulator | |
| uses: futureware-tech/simulator-action@v4 | |
| with: | |
| model: 'iPhone 16 Pro' | |
| os_version: '18.6' | |
| - name: Install app on simulator | |
| run: xcrun simctl install booted example/ios/build/Build/Products/Debug-iphonesimulator/RiveExample.app | |
| - name: Wait for simulator to be fully ready | |
| run: | | |
| echo "Waiting for simulator to be fully ready..." | |
| sleep 10 | |
| xcrun simctl list devices | grep Booted | |
| - name: Run harness tests on iOS | |
| working-directory: example | |
| timeout-minutes: 15 | |
| shell: bash --noprofile --norc -o pipefail {0} | |
| run: | | |
| # Background monitor: logs port/process state every 10s during an attempt | |
| start_monitor() { | |
| ( | |
| while true; do | |
| sleep 10 | |
| ts=$(date +%H:%M:%S) | |
| metro=$(lsof -ti:8081 2>/dev/null || echo "no") | |
| bridge=$(lsof -ti:3001 2>/dev/null || echo "no") | |
| app=$(pgrep -f RiveExample 2>/dev/null || echo "no") | |
| echo "[$ts] metro:$metro bridge:$bridge app:$app" | |
| done | |
| ) & | |
| MONITOR_PID=$! | |
| } | |
| stop_monitor() { | |
| kill $MONITOR_PID 2>/dev/null || true | |
| wait $MONITOR_PID 2>/dev/null || true | |
| } | |
| wait_for_port_free() { | |
| local port=$1 | |
| local timeout=${2:-10} | |
| local i=0 | |
| while lsof -ti:$port >/dev/null 2>&1; do | |
| if [ $i -ge $timeout ]; then | |
| echo " WARNING: port $port still in use after ${timeout}s" | |
| return 1 | |
| fi | |
| sleep 1 | |
| i=$((i + 1)) | |
| done | |
| echo " port $port is free (${i}s)" | |
| return 0 | |
| } | |
| nuke_harness() { | |
| echo "[$(date +%H:%M:%S)] cleanup: killing all harness/metro/node processes" | |
| pkill -9 -f "react-native-harness" 2>/dev/null || true | |
| pkill -9 -f "jest" 2>/dev/null || true | |
| lsof -ti:8081 | xargs kill -9 2>/dev/null || true | |
| lsof -ti:3001 | xargs kill -9 2>/dev/null || true | |
| wait_for_port_free 8081 | |
| wait_for_port_free 3001 | |
| } | |
| # Pre-warm Metro: compile the JS bundle once so all test attempts use cached transforms. | |
| # The harness starts its own Metro, but it reuses the on-disk transform cache. | |
| echo "=== Pre-warming Metro bundle ===" | |
| npx react-native start --port 8081 --no-interactive & | |
| PREWARM_PID=$! | |
| # Wait for Metro to be responsive | |
| for i in $(seq 1 30); do | |
| if curl -s http://localhost:8081/status 2>/dev/null | grep -q "packager-status:running"; then | |
| echo "Metro is running (after ${i}s)" | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| # Trigger full bundle compilation (the slow part on cold start) | |
| echo "Fetching iOS bundle to warm cache..." | |
| if curl -s --max-time 300 -o /dev/null -w "Bundle fetch: HTTP %{http_code} in %{time_total}s\n" \ | |
| "http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false"; then | |
| echo "Metro bundle cache is warm" | |
| else | |
| echo "WARNING: Metro pre-warm failed (will rely on bridgeTimeout fallback)" | |
| fi | |
| # Kill pre-warm Metro — harness needs to start its own, but cache persists on disk | |
| kill $PREWARM_PID 2>/dev/null || true | |
| wait $PREWARM_PID 2>/dev/null || true | |
| wait_for_port_free 8081 | |
| echo "=== Pre-warm complete ===" | |
| # Retry up to 5 times — bridgeTimeout (300s) handles connection failures, | |
| # testTimeout (120s) handles hung tests, step timeout-minutes (15) is the backstop | |
| for attempt in 1 2 3 4 5; do | |
| echo "=== Attempt $attempt of 5 ===" | |
| start_monitor | |
| yarn test:harness:ios --verbose --testTimeout 120000 | |
| exit_code=$? | |
| stop_monitor | |
| if [ $exit_code -eq 0 ]; then | |
| echo "Tests passed on attempt $attempt" | |
| exit 0 | |
| fi | |
| echo "Attempt $attempt failed (exit $exit_code), retrying..." | |
| nuke_harness | |
| done | |
| echo "All attempts failed" | |
| exit 1 | |
| - name: Debug - Check for console logs | |
| if: failure() || cancelled() | |
| run: | | |
| echo "=== Simulator logs (last 5m) ===" | |
| xcrun simctl spawn booted log show --predicate 'processImagePath CONTAINS "RiveExample"' --last 5m --style compact 2>&1 | tail -200 || echo "No logs found" | |
| echo "=== System log for Metro/Node ===" | |
| xcrun simctl spawn booted log show --predicate 'process CONTAINS "node" OR process CONTAINS "metro"' --last 5m --style compact 2>&1 | tail -50 || true | |
| test-harness-android: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| ANDROID_API_LEVEL: 35 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Install JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - name: Finalize Android SDK | |
| run: | | |
| /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/wrapper | |
| ~/.gradle/caches | |
| key: ${{ runner.os }}-gradle-harness-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle-harness- | |
| ${{ runner.os }}-gradle- | |
| - name: Restore Android build cache | |
| id: android-build-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: example/android/app/build | |
| key: ${{ runner.os }}-android-build-${{ env.ANDROID_API_LEVEL }}-${{ hashFiles('yarn.lock', 'android/**', 'nitrogen/generated/android/**', 'example/android/app/build.gradle', 'example/android/gradle.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-android-build-${{ env.ANDROID_API_LEVEL }}- | |
| - name: Build Android app | |
| if: steps.android-build-cache.outputs.cache-hit != 'true' | |
| working-directory: example/android | |
| env: | |
| JAVA_OPTS: "-XX:MaxHeapSize=6g" | |
| run: | | |
| ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=x86_64 | |
| - name: Save Android build cache | |
| if: steps.android-build-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: example/android/app/build | |
| key: ${{ steps.android-build-cache.outputs.cache-primary-key }} | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Run harness tests on Android | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: ${{ env.ANDROID_API_LEVEL }} | |
| arch: x86_64 | |
| target: google_apis | |
| force-avd-creation: false | |
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim | |
| disable-animations: true | |
| script: | | |
| adb install example/android/app/build/outputs/apk/debug/app-debug.apk | |
| sleep 10 | |
| cd example && for attempt in 1 2 3; do echo "Attempt $attempt of 3"; if timeout 300 env ANDROID_AVD=test yarn test:harness:android --verbose --testTimeout 120000; then echo "Tests passed on attempt $attempt"; exit 0; fi; echo "Attempt $attempt failed (exit $?), retrying..."; sleep 5; done; echo "All attempts failed"; exit 1 | |
| - name: Debug - Check logcat | |
| if: failure() || cancelled() | |
| run: | | |
| echo "=== Checking logcat for errors ===" | |
| adb logcat -d -s ReactNativeJS:* RiveExample:* RNRive:* | tail -200 || echo "No logs found" | |
| test-harness-ios-legacy: | |
| runs-on: macos-latest | |
| timeout-minutes: 90 | |
| env: | |
| XCODE_VERSION: 16.4 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Use appropriate Xcode version | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Restore cocoapods | |
| id: cocoapods-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: | | |
| **/ios/Pods | |
| key: ${{ runner.os }}-legacy-cocoapods-${{ hashFiles('example/ios/Podfile', '*.podspec') }} | |
| restore-keys: | | |
| ${{ runner.os }}-legacy-cocoapods- | |
| - name: Install cocoapods | |
| if: steps.cocoapods-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd example | |
| bundle install | |
| USE_RIVE_LEGACY=1 bundle exec pod install --project-directory=ios | |
| - name: Save cocoapods cache | |
| if: steps.cocoapods-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: | | |
| **/ios/Pods | |
| key: ${{ steps.cocoapods-cache.outputs.cache-key }} | |
| - name: Restore iOS build cache | |
| id: ios-build-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: example/ios/build | |
| key: ${{ runner.os }}-ios-legacy-build-${{ env.XCODE_VERSION }}-${{ hashFiles('yarn.lock', 'ios/**', 'nitrogen/generated/ios/**', '*.podspec', 'example/ios/Podfile', 'example/ios/RiveExample/**') }} | |
| restore-keys: | | |
| ${{ runner.os }}-ios-legacy-build-${{ env.XCODE_VERSION }}- | |
| - name: Build iOS app | |
| if: steps.ios-build-cache.outputs.cache-hit != 'true' | |
| working-directory: example/ios | |
| run: | | |
| set -o pipefail && xcodebuild \ | |
| -derivedDataPath build \ | |
| -workspace RiveExample.xcworkspace \ | |
| -scheme RiveExample \ | |
| -sdk iphonesimulator \ | |
| -configuration Debug \ | |
| build \ | |
| CODE_SIGNING_ALLOWED=NO | |
| - name: Save iOS build cache | |
| if: steps.ios-build-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: example/ios/build | |
| key: ${{ steps.ios-build-cache.outputs.cache-primary-key }} | |
| - name: Boot iOS Simulator | |
| uses: futureware-tech/simulator-action@v4 | |
| with: | |
| model: 'iPhone 16 Pro' | |
| os_version: '18.6' | |
| - name: Install app on simulator | |
| run: xcrun simctl install booted example/ios/build/Build/Products/Debug-iphonesimulator/RiveExample.app | |
| - name: Wait for simulator to be fully ready | |
| run: | | |
| echo "Waiting for simulator to be fully ready..." | |
| sleep 10 | |
| xcrun simctl list devices | grep Booted | |
| - name: Run harness tests on iOS | |
| working-directory: example | |
| run: | | |
| for attempt in 1 2 3; do | |
| echo "Attempt $attempt of 3" | |
| if yarn test:harness:ios --verbose --testTimeout 120000; then | |
| echo "Tests passed on attempt $attempt" | |
| exit 0 | |
| fi | |
| echo "Attempt $attempt failed, retrying..." | |
| sleep 5 | |
| done | |
| echo "All attempts failed" | |
| exit 1 | |
| - name: Debug - Check for console logs | |
| if: failure() | |
| run: | | |
| echo "=== Checking simulator logs for errors ===" | |
| xcrun simctl spawn booted log show --predicate 'processImagePath CONTAINS "RiveExample"' --last 5m --style compact 2>&1 | tail -200 || echo "No logs found" | |
| test-harness-android-legacy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| ANDROID_API_LEVEL: 35 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| - name: Install JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - name: Finalize Android SDK | |
| run: | | |
| /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" | |
| - name: Enable legacy Rive backend | |
| run: | | |
| echo "USE_RIVE_LEGACY=true" >> example/android/gradle.properties | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/wrapper | |
| ~/.gradle/caches | |
| key: ${{ runner.os }}-gradle-harness-legacy-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle-harness-legacy- | |
| ${{ runner.os }}-gradle-harness- | |
| ${{ runner.os }}-gradle- | |
| - name: Restore Android build cache | |
| id: android-build-cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: example/android/app/build | |
| key: ${{ runner.os }}-android-legacy-build-${{ env.ANDROID_API_LEVEL }}-${{ hashFiles('yarn.lock', 'android/**', 'nitrogen/generated/android/**', 'example/android/app/build.gradle', 'example/android/gradle.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-android-legacy-build-${{ env.ANDROID_API_LEVEL }}- | |
| - name: Build Android app | |
| if: steps.android-build-cache.outputs.cache-hit != 'true' | |
| working-directory: example/android | |
| env: | |
| JAVA_OPTS: "-XX:MaxHeapSize=6g" | |
| run: | | |
| ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=x86_64 | |
| - name: Save Android build cache | |
| if: steps.android-build-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: example/android/app/build | |
| key: ${{ steps.android-build-cache.outputs.cache-primary-key }} | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Run harness tests on Android | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: ${{ env.ANDROID_API_LEVEL }} | |
| arch: x86_64 | |
| target: google_apis | |
| force-avd-creation: false | |
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim | |
| disable-animations: true | |
| script: | | |
| adb install example/android/app/build/outputs/apk/debug/app-debug.apk | |
| sleep 10 | |
| cd example && for attempt in 1 2 3; do echo "Attempt $attempt of 3"; if timeout 300 env ANDROID_AVD=test yarn test:harness:android --verbose --testTimeout 120000; then echo "Tests passed on attempt $attempt"; exit 0; fi; echo "Attempt $attempt failed (exit $?), retrying..."; sleep 5; done; echo "All attempts failed"; exit 1 | |
| - name: Debug - Check logcat | |
| if: failure() || cancelled() | |
| run: | | |
| echo "=== Checking logcat for errors ===" | |
| adb logcat -d -s ReactNativeJS:* RiveExample:* RNRive:* | tail -200 || echo "No logs found" |