> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revyl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CI Builds

> Get your app binary into Revyl from any CI system

Patterns for getting your app binary into Revyl from any CI system.

## Pattern 1: Upload from a URL (simplest)

If your build system produces a downloadable artifact URL (EAS, Bitrise, AppCenter, S3, GCS, GitHub Actions artifacts) then you can skip the local download entirely. The Revyl backend downloads the artifact from the URL, validates, and stores the artifact server-side using this command:

```bash theme={null}
revyl build upload --url https://your-ci.com/artifacts/app-latest.apk --app "My Android App"
```

For authenticated URLs:

```bash theme={null}
revyl build upload \
  --url https://artifacts.internal.company.com/builds/app.ipa \
  --header "Authorization: Bearer $ARTIFACT_TOKEN" \
  --app "My iOS App"
```

## Pattern 2: Revyl CLI in GitHub Actions

Use the CLI after your workflow builds the app. The CLI detects GitHub Actions
PR metadata automatically. Using the PR head SHA as the version makes the label
recognizable; matching uses SCM metadata, not the version label.

```yaml theme={null}
- name: Install Revyl CLI
  run: curl -fsSL https://revyl.com/install.sh | sh

- name: Upload Build
  id: upload
  run: |
    revyl build upload \
      --file ./build/app.apk \
      --app "$REVYL_ANDROID_APP_ID" \
      --platform android \
      --version "$REVYL_PR_HEAD_SHA" \
      --yes \
      --json
  env:
    REVYL_API_KEY: ${{ secrets.REVYL_API_KEY }}
    REVYL_ANDROID_APP_ID: ${{ vars.REVYL_ANDROID_APP_ID }}
    REVYL_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Run Tests
  run: revyl workflow run smoke-tests --android-app "$REVYL_ANDROID_APP_ID"
  env:
    REVYL_API_KEY: ${{ secrets.REVYL_API_KEY }}
    REVYL_ANDROID_APP_ID: ${{ vars.REVYL_ANDROID_APP_ID }}
```

## Pattern 3: Build on CI, Upload File

Build on the runner and upload the artifact directly:

```bash theme={null}
curl -fsSL https://revyl.com/install.sh | sh
export PATH="$HOME/.revyl/bin:$PATH"
revyl build upload --file ./build/app.apk --platform android --yes
revyl workflow run smoke-tests
```

For PR matching on other runners, add `--repo owner/name --commit "$PR_HEAD_SHA"`
to the upload step. Supply your pipeline's full PR head SHA and the base GitHub
repository. These flags also work with URL
uploads and override detected SCM identity. See
[Use your own CI](/integrations/github#use-your-own-ci) for the review policy.

### iOS on macOS runners

```yaml theme={null}
jobs:
  test:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: |
          bun install
          bunx expo prebuild --platform ios
          cd ios && pod install
          cd ios && xcodebuild -workspace YourApp.xcworkspace -scheme YourApp -configuration Release -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build ARCHS=arm64
      - env:
          REVYL_API_KEY: ${{ secrets.REVYL_API_KEY }}
        run: |
          curl -fsSL https://revyl.com/install.sh | sh
          export PATH="$HOME/.revyl/bin:$PATH"
          revyl build upload --file ios/build/Build/Products/Release-iphonesimulator/*.app --platform ios --yes
          revyl workflow run smoke-tests
```

GitHub Actions includes macOS runners on all paid plans.

### Android on Linux runners

```yaml theme={null}
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: |
          bun install
          bunx expo prebuild --platform android
          cd android && ./gradlew assembleRelease
      - env:
          REVYL_API_KEY: ${{ secrets.REVYL_API_KEY }}
        run: |
          curl -fsSL https://revyl.com/install.sh | sh
          export PATH="$HOME/.revyl/bin:$PATH"
          revyl build upload --file android/app/build/outputs/apk/release/*.apk --platform android --yes
          revyl workflow run smoke-tests
```

## Pattern 4: EAS Cloud + URL Upload (no Mac, no local build)

For Expo teams, delegate the build to EAS and just ingest the result:

```bash theme={null}
ARTIFACT_URL=$(npx eas-cli build --profile development --platform ios --non-interactive --json | jq -r '.[0].artifacts.buildUrl')
revyl build upload --url "$ARTIFACT_URL" --app "My iOS App"
```

The EAS command requires Expo authentication; use `EXPO_TOKEN` for unattended
CI. The Revyl upload separately requires `REVYL_API_KEY`. Zero Mac is needed on
the customer side: Expo's cloud does the build, and Revyl ingests the artifact.

iOS artifacts named `.gz`, `.tar.gz`, and `.tgz` work with both URL and CLI file
uploads. Revyl accepts gzip-compressed tar archives and ZIP-content EAS artifacts
using those suffixes. Standalone simulator App Clips are also supported; upload
the Clip separately when you want to test it instead of its host application.

## Pattern 5: revyl build (config-driven)

If `.revyl/config.yaml` has build commands configured, build in the cloud with:

```bash theme={null}
revyl build --profile release --platform ios --json
```

This runs `setup_commands` followed by `build_commands` on a Revyl cloud
runner and registers the artifact. Configure `app_id` and pass `--profile`
and `--platform` explicitly in CI. See [Build Configuration](/remote-builds/configuration)
for cloud prerequisites and source handling.

Test and workflow `--build` continuations remain local.

## Pattern 6: Preview URL (no device at upload time)

`--preview` prints a URL that auto-starts a simulator with that exact build when
a signed-in org member opens it. Upload itself does not start a device.

```bash theme={null}
revyl build upload --file android/app/build/outputs/apk/release/*.apk \
  --platform android --yes --preview --json
```

`--json` stays the same envelope and adds optional `preview_url` on the build
object (`/sessions/launch?buildId=<id>`). Post that URL on the PR.
Opening it starts a simulator and opens the normal session viewer. The launch
page reads platform from the uploaded build's app.
Refreshing `/sessions/launch` while a start is in flight opens the same
session. After that start settles, opening the durable preview URL again
starts a new device. Refreshing the live session does not start another device.

For a link that always opens the app's most recent build instead of one pinned
upload, use `/sessions/launch?appId=<app_id>`. The latest build is resolved
each time the link is opened, so one iOS link and one Android link (one per
app) stay current as CI uploads new builds. Copy this link from the Apps page
row menu ("Copy preview link").

## CI-Friendly Flags

| Flag | Effect |
| - | - |
| `--json` | Machine-readable JSON output |
| `--preview` | Emit a `preview_url` that auto-starts a simulator when opened |
| `--no-wait` | Queue the run and exit without waiting for results |
| `--quiet` / `-q` | Suppress non-essential output |
| `--version <string>` | Tag the build with a version (default: `<branch>-<timestamp>`) |
| `--repo <owner/name>` | Base GitHub repository for upload PR matching; requires `--commit` |
| `--commit <sha>` | Full PR head commit SHA for uploads from any runner; requires `--repo` |

## Next Steps

* [Expo Build Guide](/builds/frameworks/expo) -- Expo-specific build and upload
* [CI/CD Integration](/guides/run-tests-ci-cd) -- full CI pipeline setup


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.