maestro-parallel orchestrates Maestro flows across every connected iOS simulator, iOS device and Android phone — auto-detects your build pipeline, runs flows in parallel, merges the JUnit. No glue scripts.
$ deno install --global --reload --allow-all -n maestro-parallel \
jsr:@kaln/maestro-parallel/cli┌ maestro-parallel
│
◇ configuration ──────────────────────────────────────────╮
│ cwd: /Users/tom/devenv/cms4/admin_mobile/Shoptet │
│ build: expo run:* (Release / release) via pnpm │
│ buildMode: release │
├────────────────────────────────────────────────────────────────────╯
│
◇ Build & install
│ ◇ expo run:android done (63.9s)
│ ◇ expo run:ios done (86.4s)
│
◇ Maestro tests
│ [ios:iPhone 17 Pro] [Passed] login_flow (15s)
│ [and:Pixel 6a] [Passed] login_flow (29s)
│
└ 4/4 devices passedmp builds your release artifact on the first device of each platform group, then reuse-installs the same .apk / .app on every other device. One xcodebuild run for five sims.
Rock → EAS local → expo run:* — mp picks whichever your project uses. Override declaratively with one line of config when projects ship multiple.
Android Maestro processes run in parallel, iOS sequentially (safe default) or opt-in --shard-all. Staggered process starts dodge Maestro 2.5.x log-dir races.
TTY checklist with last-selection memory. Skip with --all in CI. Broken adb devices and missing iOS sim runtimes are surfaced with actionable hints, not stack traces.
Every device writes its own report.xml plus a merged file at the top level. CI parsers see one suite — pass/fail aggregates across platforms.
Auto-detect not enough? Drop a buildAndInstallFirst into your config. The runner does the reuse-install fan-out, signal handling, log files and JUnit merge for you.