The DeviceCloud GitHub Action is a drop-in replacement for the Maestro Cloud Action. The inputs are identical where practical, so switching is straightforward.
Additionally, you can set up our GitHub App to use GitHub Checks to manage pull requests and blocking. Read more here.
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: <path_to_your_app_file>Store your API key as a GitHub Actions secret named DCD_API_KEY. You can find your API key in the console settings.
Trigger tests on pushes to your main branch and on pull requests:
on:
push:
branches: [main]
pull_request:
branches: [main]{% hint style="info" %}
If you use pull_request_target to run tests from forked PRs, explicitly check out the PR HEAD to ensure you're testing the changed code.
{% endhint %}
on:
push:
branches: [main]
pull_request_target:
branches: [main]
jobs:
run-maestro-on-dcd:
name: Run Flows on DeviceCloud
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app/build/outputs/apk/debug/app-debug.apkapp-file accepts a path or a glob pattern. When using a glob, the first matched file is used.
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: <app_name>.zipapp-file must point to an Apple silicon compatible Simulator .app build, or a zipped .zip bundle. Glob patterns are supported; the first match is used.
If you build with EAS, download the build artifact in an earlier step and pass the resulting file to app-file. For a first-class Expo experience that wires this up for you, use the dedicated EAS Workflows integration instead.
| Input | Required | Description |
|---|---|---|
api-key |
Yes | Your DeviceCloud API key. Can also be set via the DEVICE_CLOUD_API_KEY environment variable. |
| Input | Required | Default | Description |
|---|---|---|---|
app-file |
No* | — | Path or glob to the app binary (APK or .app/.zip). *Either app-file or app-binary-id is required. |
app-binary-id |
No* | — | ID of a previously uploaded app binary. Skips the upload step. |
ignore-sha-check |
No | false |
Skip the SHA hash check that prevents duplicate uploads. Not recommended. |
| Input | Required | Default | Description |
|---|---|---|---|
flows |
No | ./.maestro/ |
Path to a flow file or folder containing flows. Alias: workspace. flows takes precedence if both are set. |
workspace |
No | ./.maestro/ |
Alias for flows. |
exclude-flows |
No | — | Subdirectories to exclude when building the flow list. |
include-tags |
No | — | Only run flows with these Maestro tags (comma-separated). |
exclude-tags |
No | — | Exclude flows with these Maestro tags (comma-separated). |
config |
No | config.yaml in workspace |
Path to a custom Maestro config file. |
| Input | Required | Default | Description |
|---|---|---|---|
android-device |
No | — | Android device model. Options: pixel-6, pixel-6-pro, pixel-7, pixel-7-pro, generic-tablet. |
android-api-level |
No | 34 |
Android API level. Options: 29, 30, 31, 32, 33, 34, 35, 36. |
ios-device |
No | — | iOS device model. Options: iphone-14, iphone-15, iphone-16, iphone-16-plus, iphone-16-pro, iphone-16-pro-max, ipad-pro-6th-gen. |
ios-version |
No | 17 |
Major iOS version. Options: 16, 17, 18, 26. |
device-locale |
No | — | Device locale in ISO-639-1_ISO-3166-1 format (e.g. de_DE). See Device Locale. |
orientation |
No | 0 |
Android only. Device orientation in degrees. Options: 0, 90. |
google-play |
No | false |
Android only. Run flows against Google Play devices. |
runner-type |
No | default |
Runner type. Options: default, m1, m4. Non-default runners incur premium pricing. See Runner Types. |
See the Devices & OS Versions page for the full availability matrix.
| Input | Required | Default | Description |
|---|---|---|---|
maestro-version |
No | — | Maestro CLI version to run flows with. See Maestro Versions. |
env |
No | — | Multiline list of environment variables (KEY=value) to inject into flows. |
name |
No | Commit message | Custom name for this test run, visible in the console. |
retry |
No | 0 |
Number of retries on failure (max 2). Retries are free — same as pressing retry in the UI. |
report |
No | — | Report format. Options: junit, html. See Report Formats. |
| Input | Required | Default | Description |
|---|---|---|---|
maestro-chrome-onboarding |
No | false |
Force Maestro-based Chrome onboarding. Fixes browser-related crashes but slows tests. See Chrome Onboarding. |
android-no-snapshot |
No | false |
Force cold boot instead of snapshot boot. Automatically enabled for API level 35+. |
| Input | Required | Default | Description |
|---|---|---|---|
disable-animations |
No | false |
Disable device animations during test execution. On Android, disables system animation scales. On iOS, enables Reduce Motion. Reduces CPU load and may improve test reliability. |
The action automatically attaches Git and pull request metadata to each run, read from the GitHub Actions environment. These values are displayed in the DeviceCloud console alongside the results, so you can trace a run back to the commit or PR that triggered it — you don't need to pass branch, commit, or PR values yourself.
| Input | Required | Default | Description |
|---|---|---|---|
include-github-context |
No | true |
Automatically attach GitHub/PR context (branch, commit SHA, PR number, PR URL, run ID, repository) to the run as metadata. Set to false to opt out. |
| Input | Required | Default | Description |
|---|---|---|---|
async |
No | false |
Exit immediately without waiting for results. Returns exit code 0 regardless of test outcome. See Async Execution. |
quiet |
No | false |
Reduce console output. Useful in verbose CI environments. |
download-artifacts |
No | — | Download logs, screenshots, and videos after the run. Options: ALL, FAILED. |
json-file |
No | false |
Write test results to a JSON file (<run_name>_dcd.json or <upload_id>_dcd.json). |
debug |
No | false |
Enable verbose debug output. |
use-beta |
No | false |
Use the beta version of the DCD CLI. |
Add an id to the step to reference its outputs in later steps:
- uses: devicecloud-dev/device-cloud-for-maestro@v2
id: devicecloud
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk| Output | Description |
|---|---|
DEVICE_CLOUD_CONSOLE_URL |
URL to view the test results in the DeviceCloud console. |
DEVICE_CLOUD_FLOW_RESULTS |
JSON array with results for each flow: [{ "name": "...", "status": "PASSED" }]. |
DEVICE_CLOUD_UPLOAD_STATUS |
Overall status of the test run: PENDING, RUNNING, PASSED, FAILED, or CANCELLED. |
DEVICE_CLOUD_APP_BINARY_ID |
ID of the uploaded app binary. Reuse this in subsequent jobs to skip re-uploading. |
- uses: devicecloud-dev/device-cloud-for-maestro@v2
id: devicecloud
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
- name: Post Test Results to Slack
if: always()
uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: 'test-results'
slack-message: |
Test Run Status: ${{ steps.devicecloud.outputs.DEVICE_CLOUD_UPLOAD_STATUS }}
View Results: ${{ steps.devicecloud.outputs.DEVICE_CLOUD_CONSOLE_URL }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}- name: Save Test Results
if: always()
run: |
echo '${{ steps.devicecloud.outputs.DEVICE_CLOUD_FLOW_RESULTS }}' > test-results.json
- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results.jsonjobs:
upload-and-test:
runs-on: ubuntu-latest
outputs:
binary-id: ${{ steps.devicecloud.outputs.DEVICE_CLOUD_APP_BINARY_ID }}
steps:
- uses: devicecloud-dev/device-cloud-for-maestro@v2
id: devicecloud
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
test-another-suite:
needs: upload-and-test
runs-on: ubuntu-latest
steps:
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-binary-id: ${{ needs.upload-and-test.outputs.binary-id }}
flows: other-tests/Git and PR metadata (branch, commit SHA, PR number/URL, repository, run ID) is attached automatically — see GitHub / PR Context. To turn it off, set include-github-context: false:
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
include-github-context: falseUse async: true to start tests without blocking your pipeline. Then use dcd status to check results separately.
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
async: true
name: ${{ github.sha }}{% hint style="info" %} With the DeviceCloud GitHub App installed, an async run reports back as a pass/fail check on the pull request, so you can gate merges without keeping a runner alive to wait for results. {% endhint %}
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
include-tags: smoke, critical
exclude-tags: wip- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
env: |
USERNAME=${{ secrets.TEST_USERNAME }}
PASSWORD=${{ secrets.TEST_PASSWORD }}- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apk
json-file: true
name: my-runThis creates my-run_dcd.json:
{
"uploadId": "abcd1234-5678-efgh-9012-ijklmnopqrst",
"consoleUrl": "https://console.devicecloud.dev/results?upload=abcd1234-...",
"appBinaryId": "app-binary-5678",
"status": "PASSED",
"flowResults": [
{ "name": "login_test", "status": "PASSED" },
{ "name": "checkout_flow", "status": "PASSED" }
]
}Switch in one line — replace the uses value:
# Before
- uses: mobile-dev-inc/action-maestro-cloud@v2
with:
api-key: ${{ secrets.MCLOUD_API_KEY }}
app-file: app.apk
# After
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: app.apkAll other inputs are compatible. The only change needed (beyond the uses line) is updating your secret name from MCLOUD_API_KEY to DCD_API_KEY (or whatever name you choose when storing your DeviceCloud API key).