For the complete documentation index, see llms.txt. This page is also available as Markdown.

Cancelling Superseded Runs

Push twice in quick succession and the first run's queued tests are dead weight: nobody is waiting on their verdict, but they still sit in the queue and still cost you. --cancel-previous cancels them when the newer run is submitted.

It is opt-in and off by default. Without it, nothing about your runs changes. Requires DeviceCloud CLI 5.6.0 or later.

What it does

When a run carrying the flag is submitted, DeviceCloud looks for the previous run from the same CI context and cancels the tests of that run which have not started yet.

  • Both runs need the flag. A run is only superseded if it was itself submitted with --cancel-previous.

  • Queued tests only. Anything already running on a device finishes and reports normally — cancelling it would free no device and refund nothing.

  • Cancelled tests are refunded at 75%, the same as cancelling a not-yet-started test by hand in the console.

  • The superseded run goes quiet. It sends no completion email, Slack message or webhook, and its GitHub check, if it has one, is closed as skipped so it cannot block a pull request. dcd cloud exits 0 for it rather than failing your build; see How a superseded run reports for how each integration treats it.

What counts as "the same CI context"

The context is built from three pieces of the run's metadata. Each tab under Usage shows how to supply them.

Part
Where it comes from

Repository

gh_repo, or bb_repo on Bitbucket

Branch or PR

gh_pr_number if present, otherwise gh_branch (bb_ twins on Bitbucket)

Job

gh_check_name — the job's check name

A pull request and a push to the same branch are different contexts, which matches how GitHub Actions treats them.

If a run has no repository, or no branch and no PR, there is no context to group by and nothing is cancelled. The run's output says so rather than guessing.

Runs that report the same CI run ID never cancel each other, so jobs within one CI run are safe even before you set check names. Where the run ID comes from:

  • CLI: --metadata gh_run_id=<id>. Without it, runs have no run ID.

  • GitHub Action: the workflow run ID, attached for you.

  • Bitbucket pipe: the build number, attached for you.

  • Bitrise: the pipeline ID, or the build's ID outside a pipeline, attached for you by step 1.4.0 and later.

  • EAS Workflows: the EAS build ID, which differs between your iOS and Android build jobs, so those two jobs don't count as one run. Give each its own DCD_CHECK_NAME, or set the same DCD_GH_RUN_ID on both.

Usage

The Action attaches the repository, branch, PR and run ID for you. Requires v2.6.0 or later of the Action.

The CLI doesn't read your CI provider's environment, so pass the context yourself. --repo-name plus --branch or --pr-number are required; without them nothing is cancelled. gh_check_name scopes the group to this job, and gh_run_id stops jobs of the same CI run from cancelling each other.

The pipe attaches the repository, branch, PR and build number for you. Requires pipe 1.5.0 or later.

Set the Cancel Previous Run input (cancel_previous) to true. Requires step 1.4.0 or later.

For a repository on GitHub, the step attaches the repository, branch, PR and run ID for you. For one hosted elsewhere, add the repository and branch to the Metadata input (metadata), one per line:

Give each invocation its own GitHub Check Name (check_name) if a workflow runs the step more than once.

The wrapper passes --cancel-previous straight to the CLI and builds the context from the job's env: block: DCD_GH_REPO plus DCD_GH_BRANCH or DCD_GH_PR_NUMBER are required. Giving DCD_GH_REPO as a fixed string, as above, keeps it valid on manual runs too. Instead of a check name per job, you can set DCD_GH_RUN_ID: ${{ workflow.id }} on every job so the jobs of one workflow run count as one run. See EAS Workflows for the variables.

How a superseded run reports

The superseded run's CLI keeps waiting until any of its tests that were already running have finished, then prints:

dcd cloud then exits 0, even if one of the run's tests had already failed. Under --json, status is SUPERSEDED — a third value alongside PASSED and FAILED, so update any script that switches on it.

dcd status and GET /uploads/status still report a superseded run's status as FAILED, because its cancelled tests count against it, but add a supersededBy field with the newer run's upload ID. The CI integrations read that field, so the older job passes and its status output is SUPERSEDED:

Integration
Older job
Status output

CLI

passes (dcd cloud exits 0)

status: SUPERSEDED under --json

GitHub Action v2.6.0+

passes

DEVICE_CLOUD_UPLOAD_STATUS=SUPERSEDED

Bitrise step 1.4.0+

passes

DEVICE_CLOUD_UPLOAD_STATUS=SUPERSEDED

Bitbucket pipe 1.5.0+

passes

DEVICE_CLOUD_UPLOAD_STATUS=SUPERSEDED

EAS Workflows 1.4.0+

passes

upload_status=SUPERSEDED

Earlier versions of the integrations fail the older job, because they only see the FAILED status.

Each result cancelled this way carries a cancellation_reason of superseded_by:<upload id> in GET /results/{uploadId}.

Limitations

  • It cannot stop a test that is already running on a device. A run whose tests had all started is unaffected.

  • A re-run that reports the same CI run ID, such as GitHub's "Re-run failed jobs", does not supersede the original attempt.

  • A previous run older than 24 hours is not superseded.

Last updated