name: Automated Integration Tests # Temporarily disabled - uncomment to re-enable on: # push: # pull_request: # branches: [main] workflow_dispatch: # This is to manually trigger if needed defaults: run: shell: bash jobs: Automated_integration_test: runs-on: linux-amd64 timeout-minutes: 90 container: image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly env: STORE_PASS: test@cake_wallet KEY_PASS: test@cake_wallet MONEROC_CACHE_DIR_ROOT: /opt/generic_cache BRANCH_NAME: ${{ github.head_ref || github.ref_name }} ANDROID_AVD_HOME: /root/.android/avd volumes: - /opt/cw_cache_android/root/.cache:/root/.cache - /opt/cw_cache_android/root/.android/avd/:/root/.android/avd - /opt/cw_cache_android/root/.ccache:/root/.ccache - /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache - /opt/cw_cache_android/root/.gradle/:/root/.gradle - /opt/cw_cache_android/root/.android/:/root/.android - /opt/cw_cache_android/root/go/pkg:/root/go/pkg - /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache - /dev/kvm:/dev/kvm strategy: matrix: api-level: [29] steps: - name: Fix github actions messing up $HOME... run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV' - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: configure git run: | git config --global --add safe.directory '*' git config --global user.email "ci@cakewallet.com" git config --global user.name "CakeWallet CI" - name: Get the full commit message run: | FULL_MESSAGE="$(git log -1 --pretty=%B)" echo "message<> $GITHUB_ENV echo "$FULL_MESSAGE" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - name: Add secrets run: | touch cw_bitcoin/lib/.secrets.g.dart touch cw_evm/lib/.secrets.g.dart touch cw_solana/lib/.secrets.g.dart touch cw_core/lib/.secrets.g.dart touch cw_nano/lib/.secrets.g.dart touch cw_tron/lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart echo "const breezApiKey = '${{ secrets.BREEZ_API_KEY }}';" >> cw_bitcoin/lib/.secrets.g.dart echo "${{ secrets.MAIN_SECRETS_FILE }}" | base64 --decode > lib/.secrets.g.dart - name: prepare monero_c and cache run: | export MONEROC_HASH=$(cat scripts/prepare_moneroc.sh | grep 'git checkout' | xargs | awk '{ print $3 }') echo MONEROC_HASH=$MONEROC_HASH >> /etc/environment mkdir -p "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" pushd scripts ln -s "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" ./prepare_moneroc.sh popd pushd scripts/monero_c mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" || true mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" || true mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" || true mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" || true mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" || true mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" || true rm -rf "$PWD/contrib/depends/built" "$PWD/monero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" rm -rf "$PWD/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" mkdir -p contrib/depends || true ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" "$PWD/contrib/depends/built" ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" "$PWD/monero/contrib/depends/built" ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" "$PWD/contrib/depends/sources" ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" popd - name: Generate KeyStore run: | pushd /opt/generic_cache if [[ ! -f key.jks ]]; then keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS else echo "$PWD/key.jks exist, not generating" fi popd cp /opt/generic_cache/key.jks android/app - name: Execute Build and Setup Commands run: | pushd scripts/android source ./app_env.sh cakewallet ./app_config.sh popd - name: Build monero_c run: | pushd scripts/android/ source ./app_env.sh cakewallet ./build_monero_all.sh popd - name: Install Flutter dependencies run: | flutter pub get - name: Build mwebd run: | set -x -e export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }') echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment pushd scripts/android gomobile init; ./build_mwebd.sh popd - name: Build Decred run: | set -x -e pushd scripts/android ./build_decred.sh popd - name: Build generated code run: | flutter --version flutter clean rm -rf .dart_tool rm pubspec.lock flutter pub get ./model_generator.sh async - name: Generate key properties run: | dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS - name: Generate localization run: | dart run tool/generate_localization.dart - name: Rename app run: | sanitized_branch_name=${BRANCH_NAME#origin/} # Remove 'origin/' prefix if it exists sanitized_branch_name=${sanitized_branch_name:0:16} # Take only the first 16 characters sanitized_branch_name=$(echo "$sanitized_branch_name" | tr '[:upper:]' '[:lower:]') # Convert to lowercase sanitized_branch_name=$(echo "$sanitized_branch_name" | sed 's/[^a-z0-9]//g') # Remove all special characters echo -e "id=com.cakewallet.test_${sanitized_branch_name}\nname=${BRANCH_NAME}" > android/app.properties - name: Build run: | flutter build apk --dart-define=hasDevOptions=true --release --split-per-abi - name: Rename apk file run: | sanitized_branch_name=$(grep '^id=' android/app.properties | cut -d'=' -f2 | sed 's/com\.cakewallet\.test_//') cd build/app/outputs/flutter-apk mkdir test-apk cp app-arm64-v8a-release.apk test-apk/${sanitized_branch_name}.apk cp app-x86_64-release.apk test-apk/${sanitized_branch_name}_x86.apk echo "APK files created: test-apk/${sanitized_branch_name}.apk and test-apk/${sanitized_branch_name}_x86.apk" - name: Find APK file id: find_apk run: | set -x # Read the sanitized branch name from the app.properties file sanitized_branch_name=$(grep '^id=' android/app.properties | cut -d'=' -f2 | sed 's/com\.cakewallet\.test_//') echo "Looking for APK file: build/app/outputs/flutter-apk/test-apk/${sanitized_branch_name}.apk" ls -la build/app/outputs/flutter-apk/test-apk/ || echo "test-apk directory not found" apk_file=$(ls build/app/outputs/flutter-apk/test-apk/${sanitized_branch_name}.apk || exit 1) echo "APK_FILE=$apk_file" >> $GITHUB_ENV echo "APK file found: $apk_file" ls -la "$apk_file" - name: ๐Ÿ” Debug System Environment run: | echo "=== System Information ===" uname -a echo "=== Container Info ===" cat /proc/version || echo "Cannot read /proc/version" echo "=== KVM Device Check ===" ls -la /dev/kvm || echo "KVM device not found" echo "=== udev Status ===" which udevadm || echo "udevadm not found" echo "=== Current User ===" whoami echo "=== Groups ===" groups echo "=== Environment ===" env | grep -E "(HOME|USER|PATH)" | sort - name: ๐Ÿงน Clean up existing emulators run: | echo "=== Cleaning up existing emulators ===" # Kill any existing emulator processes pkill -f emulator || echo "No emulator processes found" # Kill any existing adb processes pkill -f adb || echo "No adb processes found" # Wait a moment for processes to terminate sleep 2 # Start adb server fresh adb start-server || echo "Failed to start adb server" # Kill adb server to ensure clean state adb kill-server || echo "Failed to kill adb server" echo "=== Cleanup complete ===" - name: ๐Ÿ” Verify AVD Configuration run: | echo "=== AVD Configuration Check ===" # Check if AVD directory exists echo "Checking AVD directory..." ls -la ~/.android/avd/ || echo "AVD directory not found" # List available AVDs echo "Available AVDs:" emulator -list-avds || echo "Failed to list AVDs" # Check Android SDK location echo "Android SDK location:" echo $ANDROID_HOME echo $ANDROID_SDK_ROOT # Check emulator binary echo "Emulator binary:" which emulator || echo "Emulator not found in PATH" emulator -version || echo "Failed to get emulator version" echo "=== AVD Check Complete ===" - name: ๐Ÿฆพ Enable KVM run: | echo "=== KVM Setup and Verification ===" # Check if KVM device exists if [ -e /dev/kvm ]; then echo "โœ… KVM device found at /dev/kvm" # Check current permissions echo "Current KVM permissions:" ls -la /dev/kvm # Set proper permissions sudo chmod 666 /dev/kvm || echo "โš ๏ธ Failed to set KVM permissions" # Verify permissions were set if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then echo "โœ… KVM is readable and writable" else echo "โš ๏ธ KVM permissions may not be optimal" fi # Test KVM functionality echo "Testing KVM functionality..." if command -v kvm-ok >/dev/null 2>&1; then if kvm-ok 2>/dev/null; then echo "โœ… KVM is working properly" else echo "โš ๏ธ KVM check failed, but continuing..." fi else echo "โ„น๏ธ kvm-ok command not available, testing manually..." # Manual KVM test if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then echo "โœ… KVM device is accessible" fi fi # Try to create udev rules (may fail in container, that's OK) echo "Setting up udev rules..." sudo mkdir -p /etc/udev/rules.d || echo "โš ๏ธ Could not create udev directory" echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules || echo "โš ๏ธ Could not create udev rule" # Try to reload udev rules (will likely fail in container, that's OK) sudo udevadm control --reload-rules || echo "โ„น๏ธ udevadm control failed (expected in container)" sudo udevadm trigger --name-match=kvm || echo "โ„น๏ธ udevadm trigger failed (expected in container)" else echo "โŒ KVM device not found at /dev/kvm" echo "This will cause the emulator to run in software mode (slower)" fi # Check system resources echo "=== System Resources ===" echo "Memory:" free -h echo "Disk space:" df -h echo "CPU cores:" nproc echo "KVM module loaded:" lsmod | grep kvm || echo "KVM module not loaded (may be built-in)" echo "=== KVM Setup Complete ===" - name: ๐Ÿฆพ Cache gradle uses: gradle/actions/setup-gradle@v3 - name: ๐Ÿฆพ Cache AVD uses: actions/cache@v4 id: avd-cache with: path: | ~/.android/avd/* ~/.android/adb* key: avd-${{ matrix.api-level }} - name: ๐Ÿ”ง Prepare emulator utilities run: | # Create a helper script for emulator readiness checks cat > /tmp/emulator_ready.sh << 'EOF' #!/bin/bash echo "=== Emulator Readiness Check ===" # Wait for boot completion echo "1. Checking boot completion..." timeout 300 bash -c 'until adb shell getprop sys.boot_completed 2>/dev/null | grep -q "1"; do sleep 5; echo " Waiting for boot completion..."; done' if [ $? -eq 0 ]; then echo "โœ… Boot completed" else echo "โŒ Boot completion timeout" echo "โš ๏ธ Continuing anyway..." fi # Wait for input service echo "2. Checking input service..." timeout 60 bash -c 'until adb shell service list 2>/dev/null | grep -q "input"; do sleep 2; echo " Waiting for input service..."; done' if [ $? -eq 0 ]; then echo "โœ… Input service ready" else echo "โŒ Input service timeout" echo "โš ๏ธ Continuing anyway..." fi # Wait for package manager echo "3. Checking package manager..." timeout 60 bash -c 'until adb shell pm list packages 2>/dev/null >/dev/null; do sleep 2; echo " Waiting for package manager..."; done' if [ $? -eq 0 ]; then echo "โœ… Package manager ready" else echo "โŒ Package manager timeout" echo "โš ๏ธ Continuing anyway..." fi # Wait for settings service echo "4. Checking settings service..." timeout 60 bash -c 'until adb shell settings get global window_animation_scale 2>/dev/null >/dev/null; do sleep 2; echo " Waiting for settings service..."; done' if [ $? -eq 0 ]; then echo "โœ… Settings service ready" else echo "โŒ Settings service timeout" echo "โš ๏ธ Continuing anyway..." fi echo "โœ… Emulator readiness check completed" EOF chmod +x /tmp/emulator_ready.sh - name: ๐Ÿฆพ Create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} force-avd-creation: false # arch: ${{ matrix.arch }} emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -read-only -memory 1024 -no-snapshot-save working-directory: ${{ github.workspace }} disable-animations: false script: | echo "=== AVD Snapshot Generation ===" # Use the helper script to ensure emulator is fully ready /tmp/emulator_ready.sh # Manually disable animations with error handling echo "Disabling animations manually..." adb shell settings put global window_animation_scale 0.0 || echo "Failed to disable window animations" adb shell settings put global transition_animation_scale 0.0 || echo "Failed to disable transition animations" adb shell settings put global animator_duration_scale 0.0 || echo "Failed to disable animator duration" echo "=== Checking emulator status before termination ===" adb devices || echo "ADB devices check failed" echo "=== AVD snapshot generation complete ===" - name: ๐Ÿš€ Integration tests on Android Emulator timeout-minutes: 60 uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} force-avd-creation: false emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -read-only -memory 1024 -no-snapshot-save disable-animations: false working-directory: ${{ github.workspace }} script: | echo "=== Pre-test Environment Check ===" pwd ls -la integration_test_runner.sh || echo "integration_test_runner.sh not found" echo "=== Emulator Status Check ===" adb devices echo "=== Emulator Info ===" adb shell getprop ro.build.version.release || echo "Failed to get Android version" adb shell getprop ro.product.model || echo "Failed to get device model" echo "=== Checking Emulator Performance ===" adb shell cat /proc/cpuinfo | grep -i "model name" | head -1 || echo "Could not check CPU info" adb shell cat /proc/meminfo | grep -i "memtotal" || echo "Could not check memory info" echo "=== Checking Emulator Process ===" ps aux | grep emulator | grep -v grep || echo "Emulator process not found in ps output" echo "=== Making script executable ===" chmod a+rx integration_test_runner.sh echo "=== Running integration tests ===" echo "Final emulator readiness check..." /tmp/emulator_ready.sh || echo "Emulator readiness check failed, but continuing..." echo "Disabling animations for test performance..." adb shell settings put global window_animation_scale 0.0 || echo "Failed to disable window animations" adb shell settings put global transition_animation_scale 0.0 || echo "Failed to disable transition animations" adb shell settings put global animator_duration_scale 0.0 || echo "Failed to disable animator duration" echo "Starting integration test runner..." ./integration_test_runner.sh TEST_EXIT_CODE=$? if [ $TEST_EXIT_CODE -eq 0 ]; then echo "Integration tests completed successfully" else echo "Integration tests failed with exit code $TEST_EXIT_CODE" fi - name: ๐Ÿงน Post-test cleanup if: always() run: | echo "=== Post-test cleanup ===" # Kill any remaining emulator processes pkill -f emulator || echo "No emulator processes to kill" # Kill any remaining adb processes pkill -f adb || echo "No adb processes to kill" # Wait for processes to terminate sleep 2 # Check for any remaining processes ps aux | grep -E "(emulator|adb)" | grep -v grep || echo "No remaining emulator/adb processes" echo "=== Cleanup complete ===" - name: cleanup run: rm -rf build/app/outputs/flutter-apk/test-apk/