---
title: GitHub PR Checks for Maestro | TestingBot
description: Post a TestingBot / tests status check on every pull request from your
  Maestro runs and block merges until every flow passes on every device.
source_url:
  html: https://testingbot.com/support/app-automate/maestro/ci-cd/github-pr-checks
  md: https://testingbot.com/support/app-automate/maestro/ci-cd/github-pr-checks.md
---

# GitHub PR Checks for Maestro

With the TestingBot GitHub App installed, every Maestro run started from a pull request posts a native `TestingBot / tests` status check on it: each flow on each device, failures first, with a link to the run in the TestingBot dashboard. Make it a [required check](https://testingbot.com#merge-gate) and a pull request cannot be merged while a Maestro flow is failing. There is nothing to register or resolve from your workflow; the [Maestro GitHub Action](https://testingbot.com/support/app-automate/maestro/ci-cd/github-actions) and the [TestingBot CLI](https://testingbot.com/support/app-automate/maestro#cli) pass the commit along and TestingBot finalizes the check when every flow has finished.

![TestingBot / tests check on a GitHub pull request for a Maestro run: All 1 flow passed on TestingBot, with a device, flow and result table linking to the run](https://testingbot.com/assets/support/github-pr-checks/maestro-check-6185df5c10dcff0db4042e811f527a36ba354a07a1fb30f4a9ebb6b2e7c5cd65.png)

## Install the GitHub App

1. In the members area, open [Integrations](https://testingbot.com/members/integrations) and choose **GitHub PR Checks**.
2. Click **Install the TestingBot GitHub App**. You are redirected to GitHub.
3. Pick the account or organization and the repositories to enable, then install. You are redirected back and the integration shows as **Connected**.

Install through TestingBot rather than from GitHub's app settings, so the installation is linked to your TestingBot account. Only account owners and admins can connect or disconnect the integration. Sub-accounts of a team share the owner's installation: a run started with a sub-account's API key posts checks as long as the app is installed under the team owner.

## Repositories and disconnecting

Checks appear only on repositories the app can reach. The integration page shows whether the app covers all repositories or a selection, with a link to change the selection on GitHub; changes apply immediately because TestingBot listens to GitHub's installation events. Disconnecting from the integration page stops checks; reconnecting later reuses the existing installation.

## Run from a pull request

With the Maestro GitHub Action a single step is enough; it fills in the repository, head commit, branch and pull request from the workflow context:

```yaml
- uses: testingbot/maestro-action@v1
  with:
    api-key: ${{ secrets.TB_KEY }}
    api-secret: ${{ secrets.TB_SECRET }}
    app-file: app/build/outputs/apk/debug/app-debug.apk
    workspace: .maestro
```

From any other CI, pass the metadata flags to the CLI. The commit SHA must be the full 40-character head commit of the pull request, not the merge commit:

```bash
npx --yes @testingbot/cli@latest maestro app.apk ./flows \
  --repo-owner "$REPO_OWNER" --repo-name "$REPO_NAME" \
  --commit-sha "$HEAD_SHA" --branch "$BRANCH" \
  --pull-request-id "$PR_NUMBER"
```

Async runs are a good fit: with `--async` (or `async: true` on the action) the CI step returns within seconds, the flows run on TestingBot, and the check is finalized when they finish, so merges are gated without holding a runner. The CLI prints `GitHub check registered for owner/repo@sha` when the link was made. If that line is missing, one of the values was absent, the commit SHA was not the full head SHA, or the app is not installed for the repository owner. In EAS Workflows the [eas-workflow wrapper](https://testingbot.com/support/app-automate/maestro/ci-cd/expo-eas) sets the same flags from `TB_GH_*` variables.

## Pull request comment

Alongside the check, TestingBot maintains one self-updating comment on the pull request with the same device, flow and result table. Pushing new commits updates the comment rather than adding new ones.

![TestingBot Maestro results comment on a pull request: all flows passed, with the device, flow and result table](https://testingbot.com/assets/support/github-pr-checks/maestro-pr-comment-b4a0e71c87de95bf678fbc8976eca5f497697a2778f0de0d0a156b0556ac65c4.png)

## Require green flows before merging

1. In your GitHub repository, go to **Settings** → **Branches** (or **Rules** → **Rulesets** ).
2. Add a rule for your default branch and enable **Require status checks to pass before merging**.
3. Select `TestingBot / tests` as a required check. GitHub lists it once it has run on the branch at least once in the last 7 days, so open one pull request first.

The check is green only when every run finished and every flow passed. A cancelled run, a failed flow or a run that never completed all conclude as failing, so the merge stays blocked until the tests are genuinely green. Flows that failed and then passed on a `--retry` count as passed; only the newest attempt per flow is judged, the same rule the dashboard and the CLI exit code use. With `--device-matrix` every device is a row in the same check.

## Several runs on one pull request

Each Maestro run posts one check, named `TestingBot / tests` by default. When more than one run tests the same commit, an iOS job and an Android job for example, give each a check name so they post separate checks that branch protection can require individually: `--check-name iOS` on the CLI, `check-name: iOS` on the action, or `TB_CHECK_NAME=iOS` with the EAS wrapper. The check is then called `TestingBot / iOS` and gets its own pull request comment.

```yaml
jobs:
  ios:
    steps:
      - uses: testingbot/maestro-action@v1
        with:
          app-file: app.zip
          check-name: iOS
  android:
    steps:
      - uses: testingbot/maestro-action@v1
        with:
          app-file: app.apk
          check-name: Android
```

Keep the name stable per job: GitHub matches required checks by name, so a name derived from the commit or run number would create a new check every time and could never be required. Two runs on the same commit with the same name share one check, and the last to finish decides its result. To cover several devices in one job, prefer `--device-matrix`; every device becomes a row in the same check.

## Troubleshooting

- **No check appears.** Look for `GitHub check registered` in the CLI output. Without it, check that the repository owner and name and a full 40-character commit SHA were passed (the Maestro GitHub Action does this for you) and that the app is installed for the repository owner.
- **The check is on the wrong commit.** For `pull_request` events pass `github.event.pull_request.head.sha`; `github.sha` is the merge commit, which is not part of the pull request.
- **The check is stuck as in-progress.** It finalizes when every run and flow has settled. If a run was cancelled or lost, a watchdog concludes the check as failing after about 50 minutes so the pull request is never left waiting.
- **The check is not selectable as a required check.** GitHub only offers it after it has run at least once on the protected branch in the last 7 days.
- **The run was started with another team's key.** The check posts only when the API key belongs to the team that installed the app. Check which key your CI secrets hold.
- **Re-running after a failure.** The check carries a **Re-run analysis** button that re-evaluates the current results. To re-run the flows themselves, use `--retry` on the run or re-run the CI job; each new run updates the check.

Browser and Appium builds use the same app with a different registration flow; see [GitHub PR Checks](https://testingbot.com/support/integrations/ci-cd/github-pr-checks).

## Frequently asked questions

### Do I need to register the build from my workflow?

No. Unlike browser builds, a Maestro run registers itself: when it carries the repository and the pull request head commit, and the app is installed, the check is created when the run starts and finalized when every flow has finished.

### When is the check green?

Only when every run finished and every flow passed. Cancelled, failed or incomplete runs conclude as failing so the merge stays blocked. A flow that passed on a retry counts as passed. See [the merge gate](https://testingbot.com#merge-gate).

### Does it work with --async?

Yes. The check is created when the run starts and finalized by TestingBot when the run completes, whether or not the CLI is still waiting.

### Can I use it from CI other than GitHub Actions?

Yes. Pass `--repo-owner`, `--repo-name`, the full `--commit-sha` and optionally `--branch` and `--pull-request-id` to the CLI from any CI. See [run from a pull request](https://testingbot.com#run).

### Can I have one check per platform on the same pull request?

Yes. Give each run a check name (`--check-name iOS`, the action's `check-name` input or `TB_CHECK_NAME` in EAS) and it posts as `TestingBot / iOS` with its own comment, so branch protection can require each platform separately. See [several runs on one pull request](https://testingbot.com#one-check).

### Why does my Maestro check not appear?

The CLI prints `GitHub check registered` when the link was made. Without that line, the metadata was incomplete, the commit SHA was not the full head SHA, or the app is not installed for the repository owner. See [troubleshooting](https://testingbot.com#troubleshooting).
