Running from CI
After your CI builds the app, Trailcam uploads the build, runs your flows on it and waits for the results. A failing flow fails the job, and on GitHub, the pull request shows each flow's result.
The project's CI tab in Trailcam has a step-by-step guide for GitHub Actions, GitLab, Bitrise, CircleCI, Codemagic, Xcode Cloud and EAS Build, with your project's ID filled in. This page explains what those guides do.
GitHub Actions
Link the repository on the project's CI tab first. Then add the Trailcam action after the step that builds your app:
name: Trailcam
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
id-token: write # Signs in with GitHub: no key to keep, once the repository is linked.
jobs:
android:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- run: ./gradlew assembleDebug
- uses: trailcam/action@v1
with:
project: <your project's ID>
build: app/build/outputs/apk/debug/app-debug.apkWith id-token: write, the action signs in with GitHub's own short-lived token for the run, so there's no key to
store. Pull requests from forks get no token, so they can't run flows.
iOS
An iOS simulator build needs a Mac, but waiting for results doesn't. GitHub bills a macOS runner's minutes at several
times a Linux one's, so build on macOS and hand the app to a Linux job. Zip it with ditto, which keeps the app's
permissions that artifacts would otherwise drop:
jobs:
ios-build:
runs-on: macos-26
steps:
- uses: actions/checkout@v7
- run: xcodebuild build -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build CODE_SIGNING_ALLOWED=NO
- run: ditto -c -k --keepParent build/Build/Products/Debug-iphonesimulator/MyApp.app MyApp.zip
- uses: actions/upload-artifact@v7
with:
name: ios-build
path: MyApp.zip
retention-days: 1
ios:
needs: ios-build
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v8
with:
name: ios-build
- uses: trailcam/action@v1
with:
project: <your project's ID>
build: MyApp.zipMore devices
Each build runs on its platform's default device, an iPhone 17 or a Pixel 8, unless the action names others. Every flow runs once on each:
- uses: trailcam/action@v1
with:
project: <your project's ID>
build: app/build/outputs/apk/debug/app-debug.apk
devices: |
pixel-8
model=pixel-8,os=11
pixel-tabletThe devices table lists them all.
Inputs and outputs
| Input | What it is |
|---|---|
project | Required. The project's ID, from its CI tab. |
build | Required. The builds to run on, one per line: an emulator APK, or an iOS simulator build (the .app folder, or a zip). |
flows | Flows to run, one name per line. Without it, every flow. |
devices | Devices to run on, one per line, as --device names them. Without it, each platform's default. |
wait | true (the default) waits for the results; false starts the runs and carries on. |
timeout-minutes | How long to wait for results (default 30). |
key | A CI key, only for a repository that isn't linked. Pass it from a secret. |
| Output | What it is |
|---|---|
result | passed, failed, couldnt_run, stopped, or running (when not waiting). |
url | The page with these results in Trailcam. |
results-id | The results' ID, for Trailcam's API. |
summary | How the runs went, in a sentence. |
To pin the action to an exact release, use its commit SHA instead of v1; Dependabot and Renovate keep pinned SHAs
up to date.
Status checks and comments
With the repository linked, each commit gets a status check per project, "Trailcam · Acme Shop", and each pull request one comment with every flow's result on each device, updated as runs finish.
- A failing flow fails the check.
- When Trailcam couldn't run a flow, the check is neutral: it's our problem, not your app's.
- Stopped runs mark the check as cancelled.
- Re-run on GitHub runs the same flows on the same builds again.
Other CI services
Everywhere else, use Trailcam's command-line tool. It needs Node 20.12 or later, and reads the commit, branch and pull request from each service's own variables:
npx --yes @trailcam/cli@1 run --project <id> --build app/build/outputs/apk/debug/app-debug.apkAdd your CI key to the service's secrets as TRAILCAM_KEY. Name one build per platform, with --build
twice for both. The tool checks each build before uploading it, so a wrong file is explained in seconds.
CI keys
A CI key (tck_…) belongs to one project. It can upload builds to that project, run its flows on the builds it
uploaded, and read the results of the runs it started; nothing else. Owners and admins make keys on the project's CI
tab. A key is shown once, when it's made, and can be revoked at any time.
How runs from CI count
Each flow on each device is one run, as anywhere else in Trailcam. Flows with errors are skipped and listed in the results instead of running. Runs that couldn't run are free and retried for you. See what counts as a run.