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.xmlCouple 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 … | bashwithout a version pin defaults to latest and turns a stable suite into a coin flip. - Emulator readiness.
adb wait-for-devicereturns before the launcher is interactive. Add amaestro testprecheck 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.