GitHub Actions
Trigger Autonomy from GitHub Actions after a preview or staging target is ready.
Use GitHub Actions when your deployment provider does not have a direct integration, when you build preview URLs in custom scripts, or when you want Autonomy to run as part of an existing CI pipeline.
Workflow placement
Place Autonomy after build, deploy, and preview URL discovery. The run should receive the final URL a reviewer would open. Keep branch filters explicit so long-running plans do not run on every push.
Required secrets
Workflow inputs
- An Autonomy project connected to the repository.
- Preview deployment URL from the hosting workflow.
- Optional protected preview bypass token.
- Pull request number or commit SHA for publishing evidence back to GitHub.
Branch and event strategy
Run short smoke plans on pull requests. Reserve broader suites for release branches, scheduled runs, or manual workflow dispatch. If the plan touches paid services or scarce test data, make the trigger explicit.
Mobile artifact handoff
GitHub Actions is also the recommended way to wire up mobile runs when the project is not on Expo EAS. The pattern is the same as for web preview URLs, but the input to Autonomy is a build artifact instead of a URL: a simulator .app for iOS or an .apk / .aab for Android.
Mobile workflow inputs
- Autonomy API key, API URL, test plan id, and platform identifiers stored as repository secrets.
- Autonomy test plan id for the mobile plan you want to run.
- For iOS: a macOS runner that can build the Simulator app.
- For Android: a Linux runner with the Android SDK to build the APK.
iOS
Split the job into two stages: build on macOS, then trigger Autonomy on Linux. Pass the .app between them as an actions artifact so you do not burn macOS minutes on a network call.
name: iOS Tests
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
build:
name: Build iOS Simulator app
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
- name: Build .app
shell: bash
run: |
set -euo pipefail
xcodebuild \
-workspace ios/MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath ios/build \
build
mkdir -p .build
cp -R ios/build/Build/Products/Release-iphonesimulator/MyApp.app .build/MyApp.app
- uses: actions/upload-artifact@v5
with:
name: ios-simulator-app
path: .build/MyApp.app
retention-days: 1
test:
name: Run Autonomy iOS plan
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v5
with:
name: ios-simulator-app
path: ./build/MyApp.app
- name: Upload and trigger
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
(cd ./build && zip -qr MyApp.app.zip MyApp.app)
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 "@./build/MyApp.app.zip" | jq -r '.storageId')
SCAN_BODY=$(jq -n \
--arg storageId "$STORAGE_ID" \
'{ storageId: $storageId, platform: "ios", fileName: "MyApp.app.zip", contentType: "application/zip" }')
curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/scan" \
-H "Authorization: Bearer $AUTONOMY_API_KEY" \
-H "Content-Type: application/json" \
-d "$SCAN_BODY"
TRIGGER_BODY=$(jq -n \
--arg testPlanId "$AUTONOMY_TEST_PLAN_ID" \
--arg storageId "$STORAGE_ID" \
--arg bundleId "$AUTONOMY_IOS_BUNDLE_ID" \
--arg branch "$GITHUB_REF_NAME" \
--arg commitSha "$GITHUB_SHA" \
'{
testPlanId: $testPlanId,
platforms: ["ios"],
branch: $branch,
targets: { ios: { storageId: $storageId, sourceMode: "upload", bundleId: $bundleId, fileName: "MyApp.app.zip" } },
deployment: { provider: "github-actions", branch: $branch, commitSha: $commitSha }
}')
curl -fsS -X POST "$AUTONOMY_API_URL/api/trigger" \
-H "Authorization: Bearer $AUTONOMY_API_KEY" \
-H "Content-Type: application/json" \
-d "$TRIGGER_BODY"Android
Android builds run on Linux, so the whole workflow fits in one job. No artifact hand-off is needed.
name: Android Tests
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
test:
name: Build and run Autonomy Android plan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Build debug APK
shell: bash
run: |
set -euo pipefail
cd android
./gradlew assembleDebug
- name: Upload and trigger
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
APK_PATH="android/app/build/outputs/apk/debug/app-debug.apk"
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/vnd.android.package-archive" \
--data-binary "@$APK_PATH" | jq -r '.storageId')
SCAN_BODY=$(jq -n \
--arg storageId "$STORAGE_ID" \
'{ storageId: $storageId, platform: "android", fileName: "app-debug.apk", contentType: "application/vnd.android.package-archive" }')
curl -fsS -X POST "$AUTONOMY_API_URL/api/artifacts/scan" \
-H "Authorization: Bearer $AUTONOMY_API_KEY" \
-H "Content-Type: application/json" \
-d "$SCAN_BODY"
TRIGGER_BODY=$(jq -n \
--arg testPlanId "$AUTONOMY_TEST_PLAN_ID" \
--arg storageId "$STORAGE_ID" \
--arg packageName "$AUTONOMY_ANDROID_PACKAGE_NAME" \
--arg branch "$GITHUB_REF_NAME" \
--arg commitSha "$GITHUB_SHA" \
'{
testPlanId: $testPlanId,
platforms: ["android"],
branch: $branch,
targets: { android: { storageId: $storageId, sourceMode: "upload", packageName: $packageName, fileName: "app-debug.apk" } },
deployment: { provider: "github-actions", branch: $branch, commitSha: $commitSha }
}')
curl -fsS -X POST "$AUTONOMY_API_URL/api/trigger" \
-H "Authorization: Bearer $AUTONOMY_API_KEY" \
-H "Content-Type: application/json" \
-d "$TRIGGER_BODY"Use a separate test plan per platform so failures point at the right surface. If the artifact is already available at a short-lived HTTPS URL, skip the upload step and pass targets.ios.artifactUrl or targets.android.artifactUrl with sourceMode: "url".
Troubleshooting
The web run starts before the preview is ready
Split deployment and QA into separate jobs and pass the deployment URL through job outputs. Autonomy should only run after the preview URL is known.
No PR comment appears
Confirm the pull request context is available, the Autonomy GitHub App is installed for the repository, and the commit SHA was passed to Autonomy.
The iOS upload is rejected as a device build
The macOS build step is producing an .ipa instead of a Simulator .app. Confirm -sdk iphonesimulator is on the xcodebuild command and the destination is a Simulator destination.
The Android build fails to find the SDK
GitHub-hosted Ubuntu runners ship with the Android SDK preinstalled. If you self-host, add actions/setup-android (or install the command-line tools) before the Gradle step.
Mobile uploads accumulate
Use explicit run targets for short-lived CI artifacts and saved environments for builds that should be reused. Reserve dashboard uploads for builds a release manager picks for production runs.