diff --git a/.github/actions/configure-keystore/action.yml b/.github/actions/configure-keystore/action.yml new file mode 100644 index 00000000..f67bfaf9 --- /dev/null +++ b/.github/actions/configure-keystore/action.yml @@ -0,0 +1,114 @@ +name: 'Configure Keystore' +description: 'Assume an AWS role and fetch a secret into environment variables' + +inputs: + aws-role-to-assume: + description: 'The AWS IAM role to assume' + required: true + aws-region: + description: 'The AWS region where the secret is stored' + required: true + secret-name: + description: 'The name of the secret in AWS Secrets Manager' + required: true + platform: + description: 'The platform for which the keystore is being configured (e.g., ios, android)' + required: true + target: + description: 'The target for which the keystore is being configured (e.g., qa, flask, main)' + required: true + +runs: + using: 'composite' + steps: + - name: Determine signing secret name + shell: bash + run: | + case "${{ inputs.target }}" in + qa) + SECRET_NAME="metamask-mobile-qa-signing-certificates" + ;; + flask) + SECRET_NAME="metamask-mobile-flask-signing-certificates" + ;; + main) + SECRET_NAME="metamask-mobile-main-signing-certificates" + ;; + *) + echo "❌ Unknown environment: ${{ inputs.environment }}" + exit 1 + ;; + esac + echo "AWS_SIGNING_CERT_SECRET_NAME=$SECRET_NAME" >> "$GITHUB_ENV" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ inputs.aws-role-to-assume }} + aws-region: ${{ inputs.aws-region }} + + - name: Fetch secret and export as environment variables + shell: bash + run: | + echo "🔐 Fetching secret from Secrets Manager..." + secret_json=$(aws secretsmanager get-secret-value \ + --region "${{ inputs.aws-region }}" \ + --secret-id "${AWS_SIGNING_CERT_SECRET_NAME}" \ + --query SecretString \ + --output text) + + keys=$(echo "$secret_json" | jq -r 'keys[]') + for key in $keys; do + value=$(echo "$secret_json" | jq -r --arg k "$key" '.[$k]') + echo "::add-mask::$value" + echo "$key=$(printf '%s' "$value")" >> "$GITHUB_ENV" + echo "✅ Set secret for key: $key" + done + + - name: Configure Android Signing Certificates + if: inputs.platform == 'android' + shell: bash + run: | + echo "📦 Configuring Android keystore..." + if [[ -z "$ANDROID_KEYSTORE" ]]; then + echo "⚠️ ANDROID_KEYSTORE is not set. Skipping keystore decoding." + exit 1 + fi + + # Use provided path if set, fallback to default + KEYSTORE_PATH="${ANDROID_KEYSTORE_PATH:-/tmp/android.keystore}" + echo "$ANDROID_KEYSTORE" | base64 --decode > "$KEYSTORE_PATH" + echo "✅ Android keystore written to $KEYSTORE_PATH" + + - name: Configure iOS Signing Certificates + if: inputs.platform == 'ios' + shell: bash + run: | + echo "📦 Configuring iOS code signing..." + + # Create paths + CERT_PATH="$RUNNER_TEMP/build_certificate.p12" + PROFILE_PATH="$RUNNER_TEMP/build_pp.mobileprovision" + KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" + CERT_PW="${IOS_SIGNING_KEYSTORE_PASSWORD}" + + # Decode base64 files + echo "$IOS_SIGNING_KEYSTORE" | base64 --decode > "$CERT_PATH" + echo "$IOS_SIGNING_PROFILE" | base64 --decode > "$PROFILE_PATH" + echo "✅ Decoded .p12 and provisioning profile" + + # Create and unlock keychain + security create-keychain -p "$CERT_PW" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$CERT_PW" "$KEYCHAIN_PATH" + + # Import cert + security import "$CERT_PATH" -P "$CERT_PW" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" > /dev/null + security set-key-partition-list -S apple-tool:,apple: -k "$CERT_PW" "$KEYCHAIN_PATH" > /dev/null + security find-identity -p codesigning "$KEYCHAIN_PATH" + + + # Install provisioning profile + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/ + echo "✅ Installed provisioning profile" diff --git a/.github/actions/setup-e2e-env-yarn-v3/action.yml b/.github/actions/setup-e2e-env-yarn-v3/action.yml new file mode 100644 index 00000000..7f9aa3b5 --- /dev/null +++ b/.github/actions/setup-e2e-env-yarn-v3/action.yml @@ -0,0 +1,339 @@ +name: 'Setup E2E Test Environment (Yarn v3)' +description: 'Sets up the environment for running E2E tests with Yarn v3 support' +inputs: + platform: + description: 'Platform (ios or android)' + required: true + node-version: + description: 'Node.js version' + required: false + default: '20.18.0' + yarn-version: + description: Yarn version to use with Corepack + required: false + default: '3.8.7' + setup-simulator: + description: 'Whether to setup simulator/emulator' + required: false + default: 'false' + bundler-version: + description: 'Bundler version to use (only for iOS)' + required: false + default: '2.5.8' + cache-prefix: + description: 'Cache key prefix' + required: false + default: 'e2e' + ruby-version: + description: Ruby version to use (only for iOS) + required: false + default: '3.1' + xcode-version: + description: Xcode version to select (e.g., 16.2) + required: false + default: '16.2' + jdk-version: + description: JDK version to use (only for Android) + required: false + default: '17' + jdk-distribution: + description: JDK distribution to use (only for Android) + required: false + default: 'temurin' + ndk-version: + description: NDK version to use (only for Android) + required: false + default: '26.1.10909125' + foundry-version: + description: Foundry version to install + required: false + default: 'v1.2.3' + android-avd-name: + description: 'Name of AVD to create and boot (for Android)' + required: false + default: 'test_e2e_avd' + android-device: + description: 'AVD device profile (e.g. "pixel")' + required: false + default: 'pixel' + android-api-level: + description: 'Android API level to use (e.g. "34")' + required: false + default: '34' + android-abi: + description: 'System architecture ABI for the Android system image (e.g. x86_64, arm64-v8a, armeabi-v7a)' + required: false + default: 'x86_64' + configure-keystores: + description: 'Whether to configure keystores for E2E tests' + required: false + default: 'true' + keystore-role-to-assume: + description: 'AWS IAM role to assume for keystore configuration' + required: false + default: 'arn:aws:iam::363762752069:role/metamask-mobile-build-signer-qa' + target: + description: 'target for which the keystore is being configured (e.g., qa, flask, main)' + required: false + default: 'qa' + + +runs: + using: 'composite' + steps: + ## Common Setup ## + - run: echo "Setup E2E Environment (Yarn v3) started" + shell: bash + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + + ## Yarn v3 Setup & Cache Management + + - name: Corepack + id: corepack + run: corepack enable && corepack prepare yarn@${{ inputs.yarn-version }} --activate + shell: bash + + - name: Restore Yarn cache + uses: actions/cache@v4 + with: + path: | + .yarn/cache + .yarn/install-state.gz + node_modules + key: ${{ inputs.cache-prefix }}-yarn-v3-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('yarn.lock', '.yarnrc.yml') }} + restore-keys: | + ${{ inputs.cache-prefix }}-yarn-v3-${{ inputs.platform }}-${{ runner.os }}- + + - name: Install JavaScript dependencies with retry + id: yarn-install + uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 + with: + timeout_minutes: 10 + max_attempts: 3 + retry_wait_seconds: 30 + command: | + if ! yarn install --immutable; then + echo "Immutable install failed, likely due to checksum mismatch. Attempting to update yarn.lock..." + yarn install + echo "yarn.lock updated. Verifying with immutable install..." + yarn install --immutable + fi + env: + NODE_OPTIONS: --max-old-space-size=4096 # Increase memory limit for Node.js due to large dependencies + + - name: Verify Detox CLI + run: yarn detox --version + + - name: Install Foundry + shell: bash + run: | + echo "Installing Foundry via foundryup..." + + export FOUNDRY_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/.foundry" + export FOUNDRY_BIN="$FOUNDRY_DIR/bin" + + mkdir -p "$FOUNDRY_BIN" + + curl -sL https://raw.githubusercontent.com/foundry-rs/foundry/master/foundryup/foundryup -o "$FOUNDRY_BIN/foundryup" + chmod +x "$FOUNDRY_BIN/foundryup" + + echo "$FOUNDRY_BIN" >> "$GITHUB_PATH" + + "$FOUNDRY_BIN/foundryup" + + ## IOS Setup ## + - name: Configure iOS Signing Certificates + if: ${{ inputs.platform == 'ios' && inputs.configure-keystores == 'true' }} + uses: MetaMask/github-tools/.github/actions/configure-keystore@e2e-env-actions + with: + aws-role-to-assume: ${{ inputs.keystore-role-to-assume }} + aws-region: 'us-east-2' + platform: 'ios' + target: ${{ inputs.target }} + + ## Ruby Setup & Cache Management + - name: Setup Ruby + if: ${{ inputs.platform == 'ios' }} + uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 + with: + ruby-version: ${{ inputs.ruby-version }} + + # Install Bundler first + - name: Install bundler + if: ${{ inputs.platform == 'ios' }} + run: gem install bundler -v ${{ inputs.bundler-version }} + shell: bash + + # Restore cached Ruby gems + - name: Restore Bundler cache + if: ${{ inputs.platform == 'ios' }} + uses: actions/cache@v4 + with: + path: ios/vendor/bundle + key: ${{ inputs.cache-prefix }}-bundler-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('ios/Gemfile.lock') }} + restore-keys: | + ${{ inputs.cache-prefix }}-bundler-${{ inputs.platform }}-${{ runner.os }}- + + # Configure bundler to use a specific path for gem installation + - name: Configure bundler install path + if: ${{ inputs.platform == 'ios' }} + run: bundle config set path 'vendor/bundle' + working-directory: ios + shell: bash + + # Install Ruby gems into ios/vendor/bundle ( cache management & awareness ) + - name: Install Ruby gems via bundler + if: ${{ inputs.platform == 'ios' }} + run: bundle install + working-directory: ios + shell: bash + + # Select Xcode version + - name: Select Xcode version + if: ${{ inputs.platform == 'ios' }} + run: sudo xcode-select -s /Applications/Xcode_${{ inputs.xcode-version }}.app/Contents/Developer + shell: bash + + # Restore CocoaPods cache + # - name: Restore CocoaPods cache + # if: ${{ inputs.platform == 'ios'}} + # uses: actions/cache@v4 + # with: + # path: ios/Pods + # key: ${{ inputs.cache-prefix }}-pods-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('ios/Podfile.lock') }} + # restore-keys: | + # ${{ inputs.cache-prefix }}-pods-${{ inputs.platform }}-${{ runner.os }}- + + # Install CocoaPods w/ cached bundler environment + - name: Install CocoaPods via bundler + if: ${{ inputs.platform == 'ios' }} + run: bundle exec pod install --repo-update --verbose + working-directory: ios + shell: bash + + - name: Install applesimutils + if: ${{ inputs.platform == 'ios' }} + run: brew tap wix/brew && brew install applesimutils + shell: bash + + - name: Check simutils + if: ${{ inputs.platform == 'ios' }} + run: xcrun simctl list devices + shell: bash + + ## Android Setup ## + + ## JDK Setup + - name: Setup Java + if: ${{ inputs.platform == 'android' }} + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + with: + java-version: ${{ inputs.jdk-version }} + distribution: ${{ inputs.jdk-distribution }} + + - name: Configure Android Signing Certificates + if: ${{ inputs.platform == 'android' && inputs.configure-keystores == 'true' }} + uses: MetaMask/github-tools/.github/actions/configure-keystore@e2e-env-actions + with: + aws-role-to-assume: ${{ inputs.keystore-role-to-assume }} + aws-region: 'us-east-2' + platform: 'android' + target: ${{ inputs.target }} + + - name: Enable KVM group perms (Ubuntu only) + if: ${{ inputs.platform == 'android' && runner.os == 'Linux' }} + 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 + shell: bash + + ## Android SDK Setup + + - name: Install required emulator dependencies + if: ${{ inputs.platform == 'android' && runner.os == 'Linux' }} + run: | + sudo apt-get update + sudo apt-get install -y \ + libpulse0 \ + libglu1-mesa \ + libnss3 \ + libxss1 + + echo "✅ Linux dependencies installed successfully" + shell: bash + + - name: Install Android SDK packages + if: ${{ inputs.platform == 'android' }} + run: | + echo "Accepting SDK licenses..." + printf 'y\n%.0s' {1..10} | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --licenses + + echo "Installing Android SDK components..." + "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --install \ + "platform-tools" \ + "platforms;android-${{ inputs.android-api-level }}" \ + "build-tools;34.0.0" \ + "emulator" \ + "system-images;android-${{ inputs.android-api-level }};google_apis;${{ inputs.android-abi }}" \ + + echo "Updating SDK packages..." + "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --update + + echo "✅ Android SDK packages installed successfully" + shell: bash + + ## NDK Setup + + - name: Debug Android SDK Paths + if: ${{ inputs.platform == 'android' }} + run: | + echo "ANDROID_HOME: $ANDROID_HOME" + echo "ANDROID_SDK_ROOT: $ANDROID_SDK_ROOT" + shell: bash + + - name: Install Android NDK + if: ${{ inputs.platform == 'android' }} + run: | + "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "ndk;${{ inputs.ndk-version }}" + shell: bash + + - name: Add Android tools to PATH + if: ${{ inputs.platform == 'android' }} + run: | + echo "$ANDROID_HOME/platform-tools" >> "$GITHUB_PATH" + echo "$ANDROID_HOME/emulator" >> "$GITHUB_PATH" + echo "$ANDROID_HOME/cmdline-tools/latest/bin" >> "$GITHUB_PATH" + shell: bash + + - name: Add NDK related toolchains to PATH + if: ${{ inputs.platform == 'android' }} + run: | + NDK_TOOLCHAIN="$ANDROID_SDK_ROOT/ndk/${{ inputs.ndk-version }}/toolchains/llvm/prebuilt/linux-x86_64/bin" + echo "$NDK_TOOLCHAIN" >> "$GITHUB_PATH" + echo "$ANDROID_SDK_ROOT/ndk/${{ inputs.ndk-version }}" >> "$GITHUB_PATH" + shell: bash + + ## Launch AVD + + - name: Set ANDROID_AVD_HOME for downstream steps + if: ${{ inputs.platform == 'android'}} + shell: bash + run: | + echo "ANDROID_AVD_HOME=$HOME/.android/avd" >> "$GITHUB_ENV" + mkdir -p "$HOME/.android/avd" + + - name: Create Android Virtual Device (AVD) + if: ${{ inputs.platform == 'android'}} + run: | + IMAGE="system-images;android-${{ inputs.android-api-level }};google_apis;${{ inputs.android-abi }}" + echo "Creating AVD with image: $IMAGE" + echo "no" | "${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager" create avd \ + --name "${{ inputs.android-avd-name }}" \ + --package "$IMAGE" \ + --device "${{ inputs.android-device }}" + shell: bash diff --git a/.github/actions/setup-e2e-env/action.yml b/.github/actions/setup-e2e-env/action.yml new file mode 100644 index 00000000..a526ae97 --- /dev/null +++ b/.github/actions/setup-e2e-env/action.yml @@ -0,0 +1,329 @@ +name: 'Setup E2E Test Environment' +description: 'Sets up the environment for running E2E tests' +inputs: + platform: + description: 'Platform (ios or android)' + required: true + node-version: + description: 'Node.js version' + required: false + default: '20.18.0' + yarn-version: + description: Yarn version to use with Corepack + required: false + default: '1.22.22' + setup-simulator: + description: 'Whether to setup simulator/emulator' + required: false + default: 'false' + bundler-version: + description: 'Bundler version to use (only for iOS)' + required: false + default: '2.5.8' + cache-prefix: + description: 'Cache key prefix' + required: false + default: 'e2e' + ruby-version: + description: Ruby version to use (only for iOS) + required: false + default: '3.1' + xcode-version: + description: Xcode version to select (e.g., 16.2) + required: false + default: '16.2' + jdk-version: + description: JDK version to use (only for Android) + required: false + default: '17' + jdk-distribution: + description: JDK distribution to use (only for Android) + required: false + default: 'temurin' + ndk-version: + description: NDK version to use (only for Android) + required: false + default: '26.1.10909125' + foundry-version: + description: Foundry version to install + required: false + default: 'v1.2.3' + android-avd-name: + description: 'Name of AVD to create and boot (for Android)' + required: false + default: 'test_e2e_avd' + android-device: + description: 'AVD device profile (e.g. "pixel")' + required: false + default: 'pixel' + android-api-level: + description: 'Android API level to use (e.g. "34")' + required: false + default: '34' + android-abi: + description: 'System architecture ABI for the Android system image (e.g. x86_64, arm64-v8a, armeabi-v7a)' + required: false + default: 'x86_64' + configure-keystores: + description: 'Whether to configure keystores for E2E tests' + required: false + default: 'true' + keystore-role-to-assume: + description: 'AWS IAM role to assume for keystore configuration' + required: false + default: 'arn:aws:iam::363762752069:role/metamask-mobile-build-signer-qa' + target: + description: 'target for which the keystore is being configured (e.g., qa, flask, main)' + required: false + default: 'qa' + + +runs: + using: 'composite' + steps: + ## Common Setup ## + - run: echo "Setup E2E Environment started" + shell: bash + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + + ## Yarn Setup & Cache Management + + - name: Corepack + id: corepack + run: corepack enable && corepack prepare yarn@${{ inputs.yarn-version }} --activate + shell: bash + + - name: Restore Yarn cache + uses: actions/cache@v4 + with: + path: | + node_modules + key: ${{ inputs.cache-prefix }}-yarn-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ inputs.cache-prefix }}-yarn-${{ inputs.platform }}-${{ runner.os }}- + + - name: Install JavaScript dependencies + id: yarn-install + run: yarn install --frozen-lockfile + shell: bash + env: + NODE_OPTIONS: --max-old-space-size=4096 # Increase memory limit for Node.js due to large dependencies + + - name: Install Detox CLI + id: install-detox-cli + run: yarn global add detox-cli + shell: bash + + - name: Install Foundry + shell: bash + run: | + echo "Installing Foundry via foundryup..." + + export FOUNDRY_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/.foundry" + export FOUNDRY_BIN="$FOUNDRY_DIR/bin" + + mkdir -p "$FOUNDRY_BIN" + + curl -sL https://raw.githubusercontent.com/foundry-rs/foundry/master/foundryup/foundryup -o "$FOUNDRY_BIN/foundryup" + chmod +x "$FOUNDRY_BIN/foundryup" + + echo "$FOUNDRY_BIN" >> "$GITHUB_PATH" + + "$FOUNDRY_BIN/foundryup" + + ## IOS Setup ## + - name: Configure iOS Signing Certificates + if: ${{ inputs.platform == 'ios' && inputs.configure-keystores == 'true' }} + uses: MetaMask/github-tools/.github/actions/configure-keystore@e2e-env-actions + with: + aws-role-to-assume: ${{ inputs.keystore-role-to-assume }} + aws-region: 'us-east-2' + platform: 'ios' + target: ${{ inputs.target }} + + ## Ruby Setup & Cache Management + - name: Setup Ruby + if: ${{ inputs.platform == 'ios' }} + uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 + with: + ruby-version: ${{ inputs.ruby-version }} + + # Install Bundler first + - name: Install bundler + if: ${{ inputs.platform == 'ios' }} + run: gem install bundler -v ${{ inputs.bundler-version }} + shell: bash + + # Restore cached Ruby gems + - name: Restore Bundler cache + if: ${{ inputs.platform == 'ios' }} + uses: actions/cache@v4 + with: + path: ios/vendor/bundle + key: ${{ inputs.cache-prefix }}-bundler-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('ios/Gemfile.lock') }} + restore-keys: | + ${{ inputs.cache-prefix }}-bundler-${{ inputs.platform }}-${{ runner.os }}- + + # Configure bundler to use a specific path for gem installation + - name: Configure bundler install path + if: ${{ inputs.platform == 'ios' }} + run: bundle config set path 'vendor/bundle' + working-directory: ios + shell: bash + + # Install Ruby gems into ios/vendor/bundle ( cache management & awareness ) + - name: Install Ruby gems via bundler + if: ${{ inputs.platform == 'ios' }} + run: bundle install + working-directory: ios + shell: bash + + # Select Xcode version + - name: Select Xcode version + if: ${{ inputs.platform == 'ios' }} + run: sudo xcode-select -s /Applications/Xcode_${{ inputs.xcode-version }}.app/Contents/Developer + shell: bash + + # Restore CocoaPods cache + # - name: Restore CocoaPods cache + # if: ${{ inputs.platform == 'ios'}} + # uses: actions/cache@v4 + # with: + # path: ios/Pods + # key: ${{ inputs.cache-prefix }}-pods-${{ inputs.platform }}-${{ runner.os }}-${{ hashFiles('ios/Podfile.lock') }} + # restore-keys: | + # ${{ inputs.cache-prefix }}-pods-${{ inputs.platform }}-${{ runner.os }}- + + # Install CocoaPods w/ cached bundler environment + - name: Install CocoaPods via bundler + if: ${{ inputs.platform == 'ios' }} + run: bundle exec pod install --repo-update --verbose + working-directory: ios + shell: bash + + - name: Install applesimutils + if: ${{ inputs.platform == 'ios' }} + run: brew tap wix/brew && brew install applesimutils + shell: bash + + - name: Check simutils + if: ${{ inputs.platform == 'ios' }} + run: xcrun simctl list devices + shell: bash + + ## Android Setup ## + + ## JDK Setup + - name: Setup Java + if: ${{ inputs.platform == 'android' }} + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + with: + java-version: ${{ inputs.jdk-version }} + distribution: ${{ inputs.jdk-distribution }} + + - name: Configure Android Signing Certificates + if: ${{ inputs.platform == 'android' && inputs.configure-keystores == 'true' }} + uses: MetaMask/github-tools/.github/actions/configure-keystore@e2e-env-actions + with: + aws-role-to-assume: ${{ inputs.keystore-role-to-assume }} + aws-region: 'us-east-2' + platform: 'android' + target: ${{ inputs.target }} + + - name: Enable KVM group perms (Ubuntu only) + if: ${{ inputs.platform == 'android' && runner.os == 'Linux' }} + 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 + shell: bash + + ## Android SDK Setup + + - name: Install required emulator dependencies + if: ${{ inputs.platform == 'android' && runner.os == 'Linux' }} + run: | + sudo apt-get update + sudo apt-get install -y \ + libpulse0 \ + libglu1-mesa \ + libnss3 \ + libxss1 + + echo "✅ Linux dependencies installed successfully" + shell: bash + + - name: Install Android SDK packages + if: ${{ inputs.platform == 'android' }} + run: | + echo "Accepting SDK licenses..." + printf 'y\n%.0s' {1..10} | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --licenses + + echo "Installing Android SDK components..." + "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --install \ + "platform-tools" \ + "platforms;android-${{ inputs.android-api-level }}" \ + "build-tools;34.0.0" \ + "emulator" \ + "system-images;android-${{ inputs.android-api-level }};google_apis;${{ inputs.android-abi }}" \ + + echo "Updating SDK packages..." + "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --update + + echo "✅ Android SDK packages installed successfully" + shell: bash + + ## NDK Setup + + - name: Debug Android SDK Paths + if: ${{ inputs.platform == 'android' }} + run: | + echo "ANDROID_HOME: $ANDROID_HOME" + echo "ANDROID_SDK_ROOT: $ANDROID_SDK_ROOT" + shell: bash + + - name: Install Android NDK + if: ${{ inputs.platform == 'android' }} + run: | + "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "ndk;${{ inputs.ndk-version }}" + shell: bash + + - name: Add Android tools to PATH + if: ${{ inputs.platform == 'android' }} + run: | + echo "$ANDROID_HOME/platform-tools" >> "$GITHUB_PATH" + echo "$ANDROID_HOME/emulator" >> "$GITHUB_PATH" + echo "$ANDROID_HOME/cmdline-tools/latest/bin" >> "$GITHUB_PATH" + shell: bash + + - name: Add NDK related toolchains to PATH + if: ${{ inputs.platform == 'android' }} + run: | + NDK_TOOLCHAIN="$ANDROID_SDK_ROOT/ndk/${{ inputs.ndk-version }}/toolchains/llvm/prebuilt/linux-x86_64/bin" + echo "$NDK_TOOLCHAIN" >> "$GITHUB_PATH" + echo "$ANDROID_SDK_ROOT/ndk/${{ inputs.ndk-version }}" >> "$GITHUB_PATH" + shell: bash + + ## Launch AVD + + - name: Set ANDROID_AVD_HOME for downstream steps + if: ${{ inputs.platform == 'android'}} + shell: bash + run: | + echo "ANDROID_AVD_HOME=$HOME/.android/avd" >> "$GITHUB_ENV" + mkdir -p "$HOME/.android/avd" + + - name: Create Android Virtual Device (AVD) + if: ${{ inputs.platform == 'android'}} + run: | + IMAGE="system-images;android-${{ inputs.android-api-level }};google_apis;${{ inputs.android-abi }}" + echo "Creating AVD with image: $IMAGE" + echo "no" | "${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager" create avd \ + --name "${{ inputs.android-avd-name }}" \ + --package "$IMAGE" \ + --device "${{ inputs.android-device }}" + shell: bash