Autonomy Docs
Integrations

Expo EAS

Build iOS Simulator and Android APK artifacts in Expo EAS, hand them off to Autonomy, and run mobile journeys on every change.

Expo EAS owns the build pipeline for Expo projects. Use this integration when you want EAS to produce the iOS Simulator .app or the Android .apk and Autonomy to run mobile journeys against it on push, pull request, or manual dispatch.

Prerequisites

Before enabling EAS runs

  • An Expo project with EAS already configured for iOS.
  • An Autonomy project linked to the repository.
  • At least one mobile test that passes against a manual upload.
  • An Autonomy test plan id for each platform the EAS workflow should trigger.

Required secrets

Store these as EAS environment variables or sensitive secrets. Do not commit them, and do not expose them through an EXPO_PUBLIC_* variable — Expo inlines those into the client bundle.

VariableRequiredDescription
AUTONOMY_API_KEYYesAutonomy API key with permission to upload builds and trigger runs.
AUTONOMY_API_URLYesAutonomy API origin, for example https://api.autonomyqa.com or your Convex site URL.
AUTONOMY_IOS_TEST_PLAN_IDNoTest plan to execute against the uploaded iOS build. Required if the workflow runs iOS.
AUTONOMY_ANDROID_TEST_PLAN_IDNoTest plan to execute against the uploaded Android build. Required if the workflow runs Android.
AUTONOMY_IOS_BUNDLE_IDNoiOS bundle identifier for the Simulator app target.
AUTONOMY_ANDROID_PACKAGE_NAMENoAndroid package name for the APK or AAB target.

You can find these in the Autonomy dashboard:

  • API key: Profile → API Keys
  • API URL: Project settings or deployment settings
  • Test plan IDs: Tests tab → plan menu → Copy ID. Use a separate plan per platform so failures point at the right surface.

Build profiles

Add or reuse build profiles in eas.json. For iOS, Autonomy needs a .app Simulator build, not a signed .ipa. The profile must set ios.simulator to true. For Android, use a development or internal-distribution profile that emits an APK.

eas.jsonjson
1{2  "build": {3    "autonomy-ios-simulator": {4      "distribution": "internal",5      "ios": {6        "simulator": true7      }8    },9    "autonomy-android-apk": {10      "distribution": "internal",11      "android": {12        "buildType": "apk"13      }14    }15  }16}

Workflow

Add .eas/workflows/autonomy-mobile-tests.yml. The workflow builds each platform you care about, downloads the artifact, uploads it through the artifact API, scans it, and triggers the matching Autonomy test plan.

The example below runs both platforms. Drop the job you do not need if you ship one platform only.

.eas/workflows/autonomy-mobile-tests.ymlyaml
name: Autonomy Mobile Tests

on:
push:
  branches: [main]
pull_request:
  branches: [main]
workflow_dispatch:

jobs:
ios:
  name: Build and test iOS with Autonomy
  type: build
  params:
    platform: ios
    profile: autonomy-ios-simulator
  steps:
    - eas/checkout
    - eas/install_node_modules
    - eas/prebuild
    - eas/build
    - eas/download_build:
        id: download
        path: ./build
    - name: Upload to Autonomy and trigger plan
      env:
        AUTONOMY_API_KEY: ${{ secrets.AUTONOMY_API_KEY }}
        AUTONOMY_API_URL: ${{ secrets.AUTONOMY_API_URL }}
        AUTONOMY_TEST_PLAN_ID: ${{ secrets.AUTONOMY_IOS_TEST_PLAN_ID }}
        AUTONOMY_IOS_BUNDLE_ID: ${{ secrets.AUTONOMY_IOS_BUNDLE_ID }}
      run: |
        set -euo pipefail
        COMMIT_SHA="${EAS_BUILD_GIT_COMMIT_HASH:-}"
        APP_PATH=$(find ./build -name "*.app" -type d | head -n 1)
        test -n "$APP_PATH"
        APP_DIR=$(dirname "$APP_PATH")
        APP_NAME=$(basename "$APP_PATH")
        (cd "$APP_DIR" && zip -qr app.zip "$APP_NAME")
        UPLOAD_URL=$(curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/upload-url" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" | jq -r '.uploadUrl')
        STORAGE_ID=$(curl -fsS -X POST "$UPLOAD_URL" \
          -H "Content-Type: application/zip" \
          --data-binary "@$APP_DIR/app.zip" | jq -r '.storageId')
        curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/scan" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg storageId "$STORAGE_ID" '{ storageId: $storageId, platform: "ios", fileName: "app.zip", contentType: "application/zip" }')"
        curl -fsS -X POST "$AUTONOMY_API_URL/api/trigger" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n \
            --arg testPlanId "$AUTONOMY_TEST_PLAN_ID" \
            --arg storageId "$STORAGE_ID" \
            --arg bundleId "$AUTONOMY_IOS_BUNDLE_ID" \
            --arg commitSha "$COMMIT_SHA" \
            '{ testPlanId: $testPlanId, platforms: ["ios"], targets: { ios: { storageId: $storageId, sourceMode: "upload", bundleId: $bundleId, fileName: "app.zip" } }, deployment: { provider: "expo-eas", commitSha: $commitSha } }')"

android:
  name: Build and test Android with Autonomy
  type: build
  params:
    platform: android
    profile: autonomy-android-apk
  steps:
    - eas/checkout
    - eas/install_node_modules
    - eas/prebuild
    - eas/build
    - eas/download_build:
        id: download
        path: ./build
    - name: Upload to Autonomy and trigger plan
      env:
        AUTONOMY_API_KEY: ${{ secrets.AUTONOMY_API_KEY }}
        AUTONOMY_API_URL: ${{ secrets.AUTONOMY_API_URL }}
        AUTONOMY_TEST_PLAN_ID: ${{ secrets.AUTONOMY_ANDROID_TEST_PLAN_ID }}
        AUTONOMY_ANDROID_PACKAGE_NAME: ${{ secrets.AUTONOMY_ANDROID_PACKAGE_NAME }}
      run: |
        set -euo pipefail
        COMMIT_SHA="${EAS_BUILD_GIT_COMMIT_HASH:-}"
        APK_PATH=$(find ./build ( -name "*.apk" -o -name "*.aab" ) | head -n 1)
        test -n "$APK_PATH"
        FILE_NAME=$(basename "$APK_PATH")
        UPLOAD_URL=$(curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/upload-url" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" | jq -r '.uploadUrl')
        STORAGE_ID=$(curl -fsS -X POST "$UPLOAD_URL" \
          -H "Content-Type: application/octet-stream" \
          --data-binary "@$APK_PATH" | jq -r '.storageId')
        curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/scan" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg storageId "$STORAGE_ID" --arg fileName "$FILE_NAME" '{ storageId: $storageId, platform: "android", fileName: $fileName, contentType: "application/octet-stream" }')"
        curl -fsS -X POST "$AUTONOMY_API_URL/api/trigger" \
          -H "Authorization: Bearer $AUTONOMY_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n \
            --arg testPlanId "$AUTONOMY_TEST_PLAN_ID" \
            --arg storageId "$STORAGE_ID" \
            --arg packageName "$AUTONOMY_ANDROID_PACKAGE_NAME" \
            --arg fileName "$FILE_NAME" \
            --arg commitSha "$COMMIT_SHA" \
            '{ testPlanId: $testPlanId, platforms: ["android"], targets: { android: { storageId: $storageId, sourceMode: "upload", packageName: $packageName, fileName: $fileName } }, deployment: { provider: "expo-eas", commitSha: $commitSha } }')"

GitHub comments and checks

Pass the commit SHA whenever the EAS or GitHub context provides one so Autonomy can post a check run on the commit. Pass the PR number only when the workflow is running for a pull request. The workflow above guards both so it stays valid on push, PR, and manual dispatch.

Troubleshooting

Autonomy rejects the iOS build as a device build

The EAS profile is producing a signed .ipa instead of a Simulator .app. Confirm ios.simulator is true on the profile the workflow runs.

EAS emits an AAB instead of an APK

For the simplest install path, the Android profile should set android.buildType to apk. Autonomy also accepts .aab, but APK is faster to install on the emulator and avoids signing surprises. If you must ship AAB, make sure the artifact upload step selects the .aab file.

Autonomy cannot post a PR comment

The Autonomy GitHub App must be installed on the repository and the workflow must pass the commit SHA. Without those, Autonomy has no anchor for the comment.

Uploads accumulate in the dashboard

Use explicit run targets for short-lived EAS artifacts and saved environments for builds that should be reused. Reserve dashboard uploads for builds a release manager picks for production runs.

On this page