refactor: Modify integration test runner for improved error handling … #152
Workflow file for this run
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: Automated Integration Tests | |
| on: [push] | |
| # pull_request: | |
| # branches: [main, Integrate-Seed-Verification-Flow-To-Integration-Tests] | |
| 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<<EOF" >> $GITHUB_ENV | |
| echo "$FULL_MESSAGE" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| - name: Add secrets | |
| run: | | |
| touch 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 | |
| if [[ "x${{ secrets.SALT }}" == "x" ]]; | |
| then | |
| echo "const salt = '954f787f12622067f7e548d9450c3832';" > lib/.secrets.g.dart | |
| else | |
| echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.KEY_CHAIN_SALT }}" == "x" ]]; | |
| then | |
| echo "const keychainSalt = '2d2beba777dbf7dff7013b7a';" >> lib/.secrets.g.dart | |
| else | |
| echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.KEY }}" == "x" ]]; | |
| then | |
| echo "const key = '638e98820ec10a2945e968435c9397a3';" >> lib/.secrets.g.dart | |
| else | |
| echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.WALLET_SALT }}" == "x" ]]; | |
| then | |
| echo "const walletSalt = '8f7f1b70';" >> lib/.secrets.g.dart | |
| else | |
| echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.SHORT_KEY }}" == "x" ]]; | |
| then | |
| echo "const shortKey = '653f270c2c152bc7ec864afe';" >> lib/.secrets.g.dart | |
| else | |
| echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.BACKUP_SALT }}" == "x" ]]; | |
| then | |
| echo "const backupSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart | |
| else | |
| echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart | |
| fi | |
| if [[ "x${{ secrets.BACKUP_KEY_CHAIN_SALT }}" == "x" ]]; | |
| then | |
| echo "const backupKeychainSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart | |
| else | |
| echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart | |
| fi | |
| echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart | |
| echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart | |
| echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart | |
| echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart | |
| echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart | |
| echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart | |
| echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> 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 chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart | |
| echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart | |
| echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart | |
| echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_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 testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart | |
| echo "const CSRFToken = '${{ secrets.CSRF_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const swapTradeExchangeMarkup = '${{ secrets.SWAPTRADE_EXCHANGE_MARKUP }}';" >> 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 meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dart | |
| echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart | |
| echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart | |
| echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart | |
| # for tests | |
| echo "const moneroTestWalletSeeds ='${{ secrets.MONERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const moneroLegacyTestWalletSeeds = '${{ secrets.MONERO_LEGACY_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const bitcoinTestWalletSeeds = '${{ secrets.BITCOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const ethereumTestWalletSeeds = '${{ secrets.ETHEREUM_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const wowneroTestWalletSeeds = '${{ secrets.WOWNERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const zanoTestWalletSeeds = '${{ secrets.ZANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart | |
| echo "const moneroTestWalletReceiveAddress = '${{ secrets.MONERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const bitcoinTestWalletReceiveAddress = '${{ secrets.BITCOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const ethereumTestWalletReceiveAddress = '${{ secrets.ETHEREUM_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const wowneroTestWalletReceiveAddress = '${{ secrets.WOWNERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart | |
| echo "const moneroTestWalletBlockHeight = '${{ secrets.MONERO_TEST_WALLET_BLOCK_HEIGHT }}';" >> lib/.secrets.g.dart | |
| # end of test secrets | |
| echo "const chainflipApiKey = '${{ secrets.CHAINFLIP_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const chainflipAffiliateFee = '${{ secrets.CHAINFLIP_AFFILIATE_FEE }}';" >> lib/.secrets.g.dart | |
| echo "const kryptonimApiKey = '${{ secrets.KRYPTONIM_API_KEY }}';" >> lib/.secrets.g.dart | |
| echo "const walletGroupSalt = '${{ secrets.WALLET_GROUP_SALT }}';" >> 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 --dont-install | |
| 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: 🦾 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 | |
| working-directory: ${{ github.workspace }} | |
| disable-animations: false | |
| script: echo "Generated AVD snapshot for caching." | |
| - 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 | |
| disable-animations: true | |
| 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 | |
| # If no emulator is running, try to start one manually | |
| if ! adb devices | grep -q emulator; then | |
| echo "No emulator found, attempting manual startup..." | |
| # List available AVDs | |
| echo "Available AVDs:" | |
| emulator -list-avds | |
| # Try to start the first available AVD | |
| AVD_NAME=$(emulator -list-avds | head -1) | |
| if [ -n "$AVD_NAME" ]; then | |
| echo "Starting AVD: $AVD_NAME" | |
| emulator -avd "$AVD_NAME" -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -read-only -memory 1024 & | |
| EMULATOR_PID=$! | |
| echo "Emulator started with PID: $EMULATOR_PID" | |
| # Wait for emulator to boot | |
| echo "Waiting for emulator to boot..." | |
| for i in {1..60}; do | |
| if adb devices | grep -q emulator; then | |
| echo "Emulator is now available" | |
| break | |
| fi | |
| echo "Waiting... ($i/60)" | |
| sleep 10 | |
| done | |
| else | |
| echo "No AVDs available" | |
| fi | |
| fi | |
| echo "=== Final Emulator Status ===" | |
| 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 ===" | |
| # Check if emulator is using hardware acceleration | |
| 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" | |
| # Check emulator process for KVM usage | |
| 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 ===" | |
| # Add error handling for the test runner | |
| if ./integration_test_runner.sh; then | |
| echo "Integration tests completed successfully" | |
| else | |
| echo "Integration tests failed with exit code $?" | |
| # Don't exit with error code to allow cleanup | |
| exit 0 | |
| fi | |
| - name: cleanup | |
| run: rm -rf build/app/outputs/flutter-apk/test-apk/ |