# Continuous integration

> Prepare a CI job or Linux container, run your tests and retain the result and evidence.

Source: https://rehearsal.dev/retest/docs/scaling-up/continuous-integration

A CI job runs the same Retest tests as your local machine, then keeps the run folder for review. Prepare its browser and app first, and let a non-zero exit fail the job.

> [!NOTE]
> **Runner requirements**
>
> Use Node.js 24.12 or later and a supported browser on macOS or Linux. See [Browsers](https://rehearsal.dev/retest/docs/platforms/browsers.md) for engine support. Native tests need the Mac toolchain and permissions in [iOS simulator apps](https://rehearsal.dev/retest/docs/platforms/ios.md) or [macOS apps](https://rehearsal.dev/retest/docs/platforms/macos.md).

## Run in a container

Retest's Linux browser checks ran in Docker with Chrome and Debian's Chromium. Use this container setup for a Linux job.

Retest keeps Chrome's sandbox enabled. A Linux container needs a non-root user, an init process and a seccomp profile that permits the sandbox's user namespaces.

Copy [Retest's seccomp profile](https://github.com/rehearsal-labs/retest/blob/a9222f303306a094257dfb2e85edeebe5287e0fb/docker/linux/chromium-seccomp.json) into your project at the path below. The image must already contain Node, Retest, a browser and your tests, and have a user named `node`.

```sh
docker run --rm --init --cap-drop ALL \
  --security-opt seccomp=docker/linux/chromium-seccomp.json \
  --user node <image> npx retest run
```

Without that profile, Chrome can stop before it answers, and Retest exits 2 with a sandbox setup failure. The init process reaps browser processes during shutdown. For large pages, increase Docker's shared memory with `--shm-size` if the default 64 MB is insufficient.

## Add a GitHub workflow

`retest init` can write a starting workflow along with the config, example test and package scripts. Use `--yes` to choose defaults without a terminal. It leaves existing files in place, so review an existing workflow yourself if the command reports that it kept one.

The generated workflow below runs directly on the host. Retest has not been checked on Linux outside Docker. On Ubuntu 23.10 and later, Chrome needs an AppArmor profile that allows user namespaces; that setup has not been tried.

```sh
npx retest init --ci github --yes
```

```yaml filename=".github/workflows/retest.yml"
name: Retest
on: [pull_request]
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 24 }
      - run: npm ci
      - run: npm run typecheck:e2e
      - run: npm run test:e2e
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: retest-runs, path: .retest/runs }
```

The generated workflow installs the project with npm, checks test types, runs Retest and uploads the run folder even after a failure. Change the dependency step for your package manager and provide the browser your config selects. The workflow does not install a browser.

## Start the app or use a preview

For a local app in the job, give Retest its start command and readiness address. Replace the command below with your app's script and port.

```ts filename="retest.config.ts"
import { chrome, defineConfig } from '@rehearsal-labs/retest'

export default defineConfig({
  apps: {
    web: chrome({
      baseUrl: 'http://127.0.0.1:4173',
      start: {
        command: 'npm run dev -- --port 4173',
        ready: 'http://127.0.0.1:4173',
      },
    }),
  },
})
```

If the readiness address already answers, Retest uses that server and leaves it running. Otherwise, it starts the command, waits for an HTTP answer and stops the server when the run ends. Any HTTP status counts as ready, so keep an application check in the test.

For a deployed preview, override the configured app's address at run time. With several apps, use the named form for each app you need to replace.

```sh
npx retest run --base-url https://preview.example.test
npx retest run --base-url web=https://preview.example.test
```

A base URL override does not remove `start`. Use a config without a local start command for preview-only jobs, or make its readiness settings appropriate to that job.

## Handle results and cancellation

Keep CI secrets in environment variables referenced by the config, and use `secret()` in test code. Retest removes variables used by `env` secrets from the test process, but that process is not a sandbox. Screenshots can contain sensitive pixels, so control access to uploaded run folders.

When `CI` has a value other than empty, `0` or `false`, `test.only` is refused before tests start. Leave that guard enabled in release checks so an accidentally narrowed suite cannot pass the job.

| Exit code | What the job should do |
| --- | --- |
| `0` | Accept the completed run. |
| `1` | Fail the job because at least one test failed. Read the result for the cause. |
| `2` | Fail the job because setup, output or another required part could not complete. |
| `130` or `143` | Treat the run as interrupted, not passed. |

If the job sends SIGTERM, Retest records interruption, writes its result, closes browsers and stops servers it started, then exits with 143. A forced kill cannot complete that cleanup. [Results and exit codes](https://rehearsal.dev/retest/docs/reference/results.md) explains mixed failures and incomplete runs.

## Keep the run folder

Upload `.retest/runs` even when tests fail. It holds results, events, logs and available screenshots or recordings. To include `report.html`, use `--reporter html` when running tests, or generate it afterwards with `retest report`. Other CI services can use the same install, run and upload sequence.

For live machine-readable output, use `--reporter jsonl`. A program can read saved runs through `readRunFolder`; a folder whose result was never written is reconstructed from events and marked incomplete.

## Next steps

- [Parallel runs](https://rehearsal.dev/retest/docs/scaling-up/parallel-runs.md): Choose concurrency appropriate to your runner.
- [Results and exit codes](https://rehearsal.dev/retest/docs/reference/results.md): Distinguish a failed check from an incomplete run.
- [Run folder and events](https://rehearsal.dev/retest/docs/reference/run-folder.md): Read the files your CI job keeps.
