This setup runs iOS on a Mac mini instance and Android on a Linux instance, one workflow each, following the rules in Mobile jobs.

Agent prompt

Add Codemagic workflows that run this project's e2e tests on iOS and Android. Docs: https://e2e.tester.army/docs/ci/codemagic.md and https://e2e.tester.army/docs/ci.md

  1. Read both pages.
  2. Add the workflows to codemagic.yaml. Adapt the project paths, scheme, and Node version to this project.
  3. Ask me to add the model key to an e2e environment variable group, marked as secret.
  4. After I start a build, read the build log and fix errors until both workflows pass.

Workflow

The Ubuntu image has AVDs named emulator (API 34), emulator-35, and emulator-36. Linux instances need billing enabled. test_report sends junit.xml to the Tests tab of the build.

codemagic.yaml
workflows:
  e2e-ios:
    name: e2e iOS
    instance_type: mac_mini_m2
    max_build_duration: 60
    environment:
      groups:
        - e2e
      node: 24
      xcode: latest
    triggering:
      events:
        - push
        - pull_request
    scripts:
      - name: Install dependencies
        script: npm ci
      - name: Install pods
        script: cd ios && pod install
      - name: Build for the simulator
        script: |
          xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp \
            -configuration Release -sdk iphonesimulator \
            -destination 'generic/platform=iOS Simulator' \
            -derivedDataPath ios/build
      - name: Boot a simulator and install the app
        script: |
          UDID=$(xcrun simctl create e2e "iPhone 17" com.apple.CoreSimulator.SimRuntime.iOS-26-5)
          xcrun simctl bootstatus "$UDID" -b
          xcrun simctl install "$UDID" ios/build/Build/Products/Release-iphonesimulator/MyApp.app
      - name: Run e2e
        script: npx e2e run --target ios --reporter list,junit
        test_report: .e2e/junit.xml
    artifacts:
      - .e2e/report.json
      - .e2e/junit.xml
      - .e2e/results/**

  e2e-android:
    name: e2e Android
    instance_type: linux_x2
    max_build_duration: 60
    environment:
      groups:
        - e2e
      node: 24
    triggering:
      events:
        - push
        - pull_request
    scripts:
      - name: Install dependencies
        script: npm ci
      - name: Build the APK
        script: cd android && ./gradlew :app:assembleRelease
      - name: Boot the emulator and install the app
        script: |
          emulator -avd emulator-35 -no-window -no-audio -no-boot-anim > /dev/null 2>&1 &
          adb wait-for-device
          until [ "$(adb shell getprop sys.boot_completed | tr -d '\r')" = "1" ]; do sleep 2; done
          adb install -r android/app/build/outputs/apk/release/app-release.apk
      - name: Run e2e
        script: npx e2e run --target android --reporter list,junit
        test_report: .e2e/junit.xml
    artifacts:
      - .e2e/report.json
      - .e2e/junit.xml
      - .e2e/results/**

Model key

Put the model key in the e2e environment variable group, marked as secret. Each build that reads the group can read the key, untrusted pull request code included. Trigger pull request builds with this group only for contributors you trust. See Model credentials.

Next