Pipeline overview
- Push to main triggers a GitHub Action workflow
- The workflow installs dependencies, installs EAS CLI, then runs an EAS build with a
build-and-maestro-test
profile - Artifacts and logs are uploaded for debugging.
Note: I led the creation of our mobile app CI pipeline.
EAS Custom Builds vs Maestro Cloud (cost-driven choice)
I implemented both EAS‑based and Maestro Cloud pipelines and validated they work
We run on EAS Build because it's significantly cheaper. As of Aug 24, 2025: EAS Starter Plan starts at $19/month; Maestro Cloud is about $212.50/month
EAS Custom Builds Configuration
Here is our build-and-maestro-test
configuration (eas.json):
"build-and-maestro-test": { "withoutCredentials": true, "config": "build-and-maestro-test.yml", "android": { "buildType": "apk", "image": "latest" }, "ios": { "simulator": true, "image": "latest" }, "channel": "build-and-maestro-test" }
Here is the corresponding Github Actions workflow file:
name: EAS Build & Maestro Tests on: push: branches: - "main" pull_request: branches: - "main" jobs: build_and_test: runs-on: ubuntu-latest timeout-minutes: 120 steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Bun uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Install dependencies run: bun install - name: Install EAS CLI run: npm install -g eas-cli - name: Run EAS Build with Maestro Tests run: EXPO_TOKEN=${{ secrets.EXPO_TOKEN }} eas build --platform android --profile build-and-maestro-test --non-interactive
Maestro Cloud Configuration
Here is the Github Actions workflow file for this:
name: EAS Build & Maestro Tests on: push: branches: - "**" jobs: build_and_test: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Bun uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Install dependencies run: bun install - name: Install EAS CLI run: npm install -g eas-cli - name: Fetch Latest EAS Build Artifact run: | # Export Expo token for authentication export EXPO_TOKEN=${{ secrets.EXPO_TOKEN }} # Get latest build URL BUILDS_JSON=$(eas build:list --platform android --profile production --status finished --json --non-interactive) # Validate JSON response if [[ -z "$BUILDS_JSON" || "$BUILDS_JSON" == "[]" ]]; then echo "No completed builds found!" exit 1 fi # Extract the build URL BUILD_URL=$(echo "$BUILDS_JSON" | jq -r '.[0].artifacts.buildUrl') # Ensure a valid URL was extracted if [[ -z "$BUILD_URL" || "$BUILD_URL" == "null" ]]; then echo "No valid build URL found!" exit 1 fi echo "Downloading APK from $BUILD_URL" # Download the APK using Expo authentication curl -L -o app-release.apk $BUILD_URL echo "APK downloaded to app-release.apk" ls -la - name: Install Maestro CLI run: | curl -Ls "https://get.maestro.mobile.dev" | bash echo "$HOME/.maestro/bin" >> $GITHUB_PATH - name: Run Maestro Cloud Tests on APK uses: mobile-dev-inc/action-maestro-cloud@v1.9.6 with: api-key: ${{ secrets.MAESTRO_API_KEY }} project-id: ${{ secrets.PROJECT_ID }} app-file: app-release.apk workspace: maestro android-api-level: 33 env: | MAESTRO_TEST_EMAIL=${{ secrets.MAESTRO_TEST_EMAIL }} MAESTRO_TEST_PASSWORD=${{ secrets.MAESTRO_TEST_PASSWORD }}
Tips and tricks from our experience
These tests can be flaky:
- Add short delays before interacting with components in dialogs
- Use pixel coordinates when IDs aren't reliable
- Use runScript for dynamic data
- I tested locally on a Pixel 5 emulator (API 33)
Top comments (0)