Maestro Deck
Guides

CI/CD integration

Run Maestro flows in GitHub Actions, GitLab CI, and CircleCI with Maestro Deck.

Maestro Deck flows are plain Maestro YAML, so any CI runner that can install the Maestro CLI and boot a simulator or emulator can execute them. This guide walks through GitHub Actions, GitLab CI, and CircleCI configurations and the gotchas each platform throws at you.

TL;DR

The recipe is the same on every provider: (1) check out the repo, (2) install the Maestro CLI at the version pinned in your project, (3) start a simulator (iOS) or emulator (Android), (4) install the app build under test, (5) run maestro test .maestro/. The differences are how each provider gives you a runtime with simulators or emulators.

GitHub Actions — iOS

GitHub-hosted macos-latest runners ship with Xcode and simulators preinstalled.

name: e2e-ios
on: [pull_request]
jobs:
  ios:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4
      - name: Install Maestro
        run: curl -Ls "https://get.maestro.mobile.dev" | bash
      - name: Boot simulator
        run: |
          xcrun simctl boot "iPhone 15" || true
          xcrun simctl bootstatus "iPhone 15" -b
      - name: Install app
        run: xcrun simctl install booted ./build/MyApp.app
      - name: Run flows
        run: ~/.maestro/bin/maestro test .maestro/

Pin a Maestro version in your repo (e.g. .maestro-version) and read it in the install step instead of fetching latest. Otherwise a CLI release can break PRs unrelated to your changes.

GitHub Actions — Android

Use reactivecircus/android-emulator-runner — it caches the AVD and avoids the 5-minute boot cost on every run.

name: e2e-android
on: [pull_request]
jobs:
  android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: 'temurin', java-version: '17' }
      - name: Install Maestro
        run: curl -Ls "https://get.maestro.mobile.dev" | bash
      - name: Run flows in emulator
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 33
          arch: x86_64
          script: |
            adb install ./build/app-debug.apk
            ~/.maestro/bin/maestro test .maestro/

GitLab CI

GitLab does not provide hosted macOS runners on the free tier. For Android, the shared Linux runners with KVM enabled work:

e2e-android:
  image: reactivecircus/android-emulator:33
  script:
    - curl -Ls "https://get.maestro.mobile.dev" | bash
    - emulator -avd test -no-window -no-audio &
    - adb wait-for-device
    - adb install ./build/app-debug.apk
    - ~/.maestro/bin/maestro test .maestro/

For iOS, register a self-hosted macOS runner. There is no hosted alternative.

CircleCI

CircleCI has first-party macOS executors and an Android machine image.

version: 2.1
jobs:
  ios:
    macos:
      xcode: 15.4.0
    steps:
      - checkout
      - run: curl -Ls "https://get.maestro.mobile.dev" | bash
      - run: xcrun simctl boot "iPhone 15"
      - run: xcrun simctl install booted ./build/MyApp.app
      - run: ~/.maestro/bin/maestro test .maestro/

Reporting

maestro test writes a JUnit report when given --format junit --output report.xml. Upload it as a CI artifact and most providers will render a per-test view in the PR.

maestro test .maestro/ --format junit --output report.xml

Couple this with --debug-output to capture per-step screenshots; you will need them when something fails on the runner but not on your laptop.

Sharding

For suites larger than ~30 flows, run shards in parallel:

maestro test .maestro/ --shards 4 --shard-index ${CI_NODE_INDEX}

Most CI providers expose a node index variable for matrix or parallelism builds. See the parallel execution guide for tradeoffs.

Common pitfalls

  • Version drift. Always pin the CLI version. curl … | bash without a version pin defaults to latest and turns a stable suite into a coin flip.
  • Emulator readiness. adb wait-for-device returns before the launcher is interactive. Add a maestro test precheck step or sleep 10s.
  • Disk space. Android emulators eat 10–20 GB. On free tiers you may have to clean caches between jobs.
  • Network. If your app talks to a staging backend, lock down the URL via env var rather than baking it into the APK / IPA.

On this page