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
skippedso it cannot block a pull request.dcd cloudexits0for 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.
WARNING
Set a check name per job. The check name is what keeps your iOS job from cancelling your Android job. If one commit runs tests more than once and those runs share a check name, they share a group — and the second will cancel the first's queued tests. Without a check name, every job on the branch or PR shares one group, and the submit output warns you.
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 sameDCD_GH_RUN_IDon both.
Usage
- uses: devicecloud-dev/device-cloud-for-maestro@v2
with:
api-key: ${{ secrets.DCD_API_KEY }}
app-file: build/app.apk
cancel-previous: true
check-name: AndroidThe Action attaches the repository, branch, PR and run ID for you. Requires v2.6.0 or later of the Action.
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:
⚠ Run superseded by a newer run from the same CI context — exiting 0
⎿ superseded by https://console.devicecloud.dev/results?upload=<newer upload id>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.
