CI/CD Integration
The TestingBot CLI integrates seamlessly with any CI/CD environment. It runs all your Maestro flows, streams the output in real-time and exits with the appropriate exit code for pipeline automation.
We recommend using npx to run the CLI without installing it globally. This ensures you're always using the latest version.
Quick Start
Run Maestro tests in any CI/CD service with a single command:
npx --yes @testingbot/cli@latest maestro app.apk ./flows \
--device "Pixel 8" \
--deviceVersion "14" \
--api-key $TB_KEY \
--api-secret $TB_SECRET
This command will:
- Upload your mobile app (
.apk,.aab,.ipaor.app) - Upload and run all Maestro flow files from the
./flowsdirectory - Stream test progress to your CI logs
- Exit with code
0on success, non-zero on failure
You can pass credentials as arguments (--api-key and --api-secret) or set them as environment variables. Environment variables are the recommended approach for CI/CD.
Environment Variables
Instead of passing credentials as command arguments, you can set them as environment variables. This is the most secure approach for CI/CD pipelines since credentials are never exposed in logs or command history.
| Variable | Description |
|---|---|
TB_KEY |
Your TestingBot API key |
TB_SECRET |
Your TestingBot API secret |
When these environment variables are set, you can omit the credential arguments entirely:
export TB_KEY="your_api_key"
export TB_SECRET="your_api_secret"
npx --yes @testingbot/cli@latest maestro app.apk ./flows \
--device "Pixel 8" \
--deviceVersion "14"
Never hardcode credentials in your CI configuration files. Always use your CI provider's secrets management feature to store TB_KEY and TB_SECRET.
Exit Codes
The CLI uses standard exit codes for CI/CD integration. Your pipeline will automatically fail when tests fail:
| Exit Code | Description |
|---|---|
0 |
All tests passed successfully |
1 |
One or more tests failed, or an error occurred |
Test Reports
Generate test reports for CI/CD result decoration and test analytics. The CLI supports both JUnit XML and HTML report formats.
JUnit Report
Generate JUnit XML reports for integration with CI/CD test result visualization:
npx --yes @testingbot/cli@latest maestro app.apk ./flows \
--device "Pixel 8" \
--deviceVersion "14" \
--report junit \
--report-output-dir ./test-results
| Option | Description |
|---|---|
--report <format> |
Report format: junit
|
--report-output-dir <path> |
Directory to save test reports (required when --report is used) |
JUnit reports can be used with most CI/CD platforms for:
- Test result visualization in your pipeline
- Tracking test trends over time
- Annotating pull requests with test results
- Integration with test management tools
You can also fetch reports via the REST API if you need to retrieve them separately.
CI/CD Metadata
Pass repository and commit information to link test runs with your source control. This enables better traceability and integration with your development workflow.
| Option | Description |
|---|---|
--commit-sha <sha> |
The commit SHA of this upload |
--pull-request-id <id> |
The ID of the pull request this upload originated from |
--repo-name <name> |
Repository name (e.g., GitHub repo slug) |
--repo-owner <owner> |
Repository owner (e.g., GitHub organization or user) |
Example with metadata:
npx --yes @testingbot/cli@latest maestro app.apk ./flows \
--device "Pixel 8" \
--deviceVersion "14" \
--commit-sha "$COMMIT_SHA" \
--pull-request-id "$PR_NUMBER" \
--repo-owner "your-org" \
--repo-name "your-repo"
View Result Details
A link to the current upload will be printed to your CI logs during execution. You can view any ongoing or past uploads in the Maestro Console.
The console provides detailed information including:
- Real-time test execution status
- Video recordings of each test run
- Screenshots captured during tests
- Device logs and Maestro output
- Test artifacts for debugging
GitHub Actions
Add Maestro testing to your GitHub Actions workflow. Store your credentials in Settings → Secrets and variables → Actions as repository secrets.
name: Maestro Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
maestro-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build Android App
run: ./gradlew assembleDebug
- name: Run Maestro Tests
run: |
npx --yes @testingbot/cli@latest maestro \
app/build/outputs/apk/debug/app-debug.apk \
./maestro-flows \
--device "Pixel 8" \
--deviceVersion "14" \
--report junit \
--report-output-dir ./test-results \
--commit-sha ${{ github.sha }} \
--pull-request-id ${{ github.event.pull_request.number }} \
--repo-owner ${{ github.repository_owner }} \
--repo-name ${{ github.event.repository.name }}
env:
TB_KEY: ${{ secrets.TB_KEY }}
TB_SECRET: ${{ secrets.TB_SECRET }}
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: maestro-results
path: test-results/
- name: Publish Test Results
uses: dorny/test-reporter@v1
if: always()
with:
name: Maestro Test Results
path: test-results/*.xml
reporter: java-junit
Add TB_KEY and TB_SECRET as repository secrets in your GitHub repository settings. Never commit credentials to your repository.
GitLab CI
Add Maestro testing to your .gitlab-ci.yml. Store your credentials in Settings → CI/CD → Variables and mark them as protected and masked.
stages:
- build
- test
build:
stage: build
image: gradle:8-jdk17
script:
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/apk/debug/
maestro-tests:
stage: test
image: node:20
dependencies:
- build
script:
- npx --yes @testingbot/cli@latest maestro
app/build/outputs/apk/debug/app-debug.apk
./maestro-flows
--device "Pixel 8"
--deviceVersion "14"
--report junit
--report-output-dir ./test-results
--commit-sha $CI_COMMIT_SHA
--pull-request-id $CI_MERGE_REQUEST_IID
--repo-owner $CI_PROJECT_NAMESPACE
--repo-name $CI_PROJECT_NAME
variables:
TB_KEY: $TB_KEY
TB_SECRET: $TB_SECRET
artifacts:
when: always
paths:
- test-results/
reports:
junit: test-results/*.xml
Add TB_KEY and TB_SECRET as CI/CD variables in your GitLab project settings. Enable "Mask variable" to prevent them from appearing in job logs.
Jenkins
Add Maestro testing to your Jenkins pipeline (Jenkinsfile). Store your credentials using Manage Jenkins → Manage Credentials as secret text.
pipeline {
agent any
environment {
TB_KEY = credentials('testingbot-api-key')
TB_SECRET = credentials('testingbot-api-secret')
}
stages {
stage('Build') {
steps {
sh './gradlew assembleDebug'
}
}
stage('Maestro Tests') {
steps {
sh """
npx --yes @testingbot/cli@latest maestro \\
app/build/outputs/apk/debug/app-debug.apk \\
./maestro-flows \\
--device "Pixel 8" \\
--deviceVersion "14" \\
--report junit \\
--report-output-dir test-results \\
--commit-sha ${env.GIT_COMMIT} \\
--pull-request-id ${env.CHANGE_ID ?: ''} \\
--repo-owner "your-org" \\
--repo-name "your-repo"
"""
}
post {
always {
junit 'test-results/*.xml'
archiveArtifacts artifacts: 'test-results/**', allowEmptyArchive: true
}
}
}
}
}
Create credentials of type "Secret text" in Jenkins for TB_KEY and TB_SECRET. Reference them using the credentials() helper in your pipeline.
CircleCI
Add Maestro testing to your .circleci/config.yml. Store your credentials in Project Settings → Environment Variables.
version: 2.1
jobs:
maestro-tests:
docker:
- image: cimg/node:20
steps:
- checkout
- run:
name: Run Maestro Tests
command: |
npx --yes @testingbot/cli@latest maestro \
./app.apk \
./maestro-flows \
--device "Pixel 8" \
--deviceVersion "14" \
--report junit \
--report-output-dir test-results \
--commit-sha $CIRCLE_SHA1 \
--pull-request-id $CIRCLE_PR_NUMBER \
--repo-owner $CIRCLE_PROJECT_USERNAME \
--repo-name $CIRCLE_PROJECT_REPONAME
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: maestro-results
workflows:
test:
jobs:
- maestro-tests
Add TB_KEY and TB_SECRET as environment variables in your CircleCI project settings. CircleCI automatically masks these values in build output.
Bitbucket Pipelines
Add Maestro testing to your bitbucket-pipelines.yml. Store your credentials in Repository settings → Pipelines → Repository variables and enable "Secured".
image: node:20
pipelines:
default:
- step:
name: Build and Test
caches:
- node
script:
- ./gradlew assembleDebug
- npx --yes @testingbot/cli@latest maestro
app/build/outputs/apk/debug/app-debug.apk
./maestro-flows
--device "Pixel 8"
--deviceVersion "14"
--report junit
--report-output-dir test-results
--commit-sha $BITBUCKET_COMMIT
--pull-request-id $BITBUCKET_PR_ID
--repo-owner $BITBUCKET_WORKSPACE
--repo-name $BITBUCKET_REPO_SLUG
artifacts:
- test-results/**
Add TB_KEY and TB_SECRET as secured repository variables in Bitbucket. Secured variables are encrypted and masked in build logs.