Skip to main content

GitHub Action

Trigger a testsbot test plan (suite) from a GitHub Actions workflow and, by default, block the job until the plan finishes. The action is a thin composite wrapper over the CLI's plans run command — it inherits the same auth, timeout, and CI exit-code behavior.

Quick start

# .github/workflows/qa.yml
name: QA
on: [pull_request]

jobs:
test-plan:
runs-on: ubuntu-latest
steps:
- uses: testsbot/run-action@v1
with:
api_token: ${{ secrets.TESTSBOT_API_TOKEN }}
test_plan_id: 00000000-0000-0000-0000-000000000000
blocking: "true"

Create the API key in the app under Settings → API keys (settings/api-keys). Keys start with tb_live_ and need the read/write scope to trigger runs. Store it as the TESTSBOT_API_TOKEN repository secret — the action passes it to the CLI purely through the environment and never prints it to the logs.

Inputs

InputRequiredDefaultDescription
api_tokenyestestsbot API key (tb_live_…, read/write scope). Pass it from a secret.
test_plan_idyesTest plan id (UUID) to run.
blockingno"true"When true, wait for the plan and fail the step on a failed result.
api_urlnohttps://api.testsbot.comAPI base URL (override for staging / self-hosted stacks).
timeoutno"600"Blocking deadline in seconds. Ignored when blocking is false.
run_modenoOptional run-mode override (ci | nightly). Defaults to the plan setting.

Outputs

OutputDescription
run_created"true" when the plan run was created, "false" otherwise.
run_idThe created plan run id.
run_urlDeep link to the plan run (<api_url>/plan-runs/<id>).
run_statusFinal status in blocking mode (running | done); empty otherwise.
run_resultFinal result in blocking mode (passed | failed); empty otherwise.

Blocking and exit behavior

In blocking mode the action polls the plan run until it is done and maps the result to the step's exit code — the same contract as runs follow:

Result / stateStep exit codeEffect
passed0Step succeeds; downstream jobs run.
failed1Step fails; downstream jobs are blocked.
timeout2Step fails after timeout seconds.

A failed plan run therefore blocks any deployment that lists the test step in its needs:.

PR gating example

Block a deploy job until the test plan passes:

name: Deploy
on:
push:
branches: [main]

jobs:
test-plan:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.qa.outputs.run_result }}
steps:
- id: qa
uses: testsbot/run-action@v1
with:
api_token: ${{ secrets.TESTSBOT_API_TOKEN }}
test_plan_id: 00000000-0000-0000-0000-000000000000
blocking: "true"
timeout: "900"

deploy:
needs: test-plan # only runs when the test-plan step exits 0
runs-on: ubuntu-latest
steps:
- run: echo "Tests passed (${{ needs.test-plan.outputs.result }}) — deploying"

Fire-and-forget example

Trigger the plan without waiting (blocking: false) — for example to kick off a nightly suite. The step exits 0 immediately and run_result is empty:

- uses: testsbot/run-action@v1
with:
api_token: ${{ secrets.TESTSBOT_API_TOKEN }}
test_plan_id: 00000000-0000-0000-0000-000000000000
blocking: "false"
run_mode: nightly

Using the CLI directly

The action wraps these CLI commands, which you can also run locally or in any CI:

# Trigger a plan and block until it finishes (CI exit codes 0/1/2)
npx @testsbot/cli plans run <plan-id>

# Trigger only, do not wait
npx @testsbot/cli plans run <plan-id> --no-block

# Follow an already-running plan run
npx @testsbot/cli plans follow <plan-run-id>

The CLI reads the API key from QA_CLONE_API_TOKEN (what the action sets) or from ~/.qa-clone/config.json (written by qa-clone auth). See the CLI docs for details.

note

The action is a thin wrapper over the CLI, which is itself a thin wrapper over the API.