# Run shotlist in CI

This guide adds a job that re-takes every screenshot on each pull request and fails when any of them no longer matches what is committed.

It assumes your screenshots are already published with `--install` and committed, along with `shotlist.baseline.json`. If they are not, work through [Keeping a screenshot current](/docs/tutorials/keeping-a-screenshot-current/) first.

## Step 1: make the job start the application

Nothing is listening on a fresh runner, so the configuration has to say how to start it:

shotlist.config.yaml

```
site:
  url: http://localhost:4321
  serve:
    command: npm run preview
    ready: 4321
    timeout: 60000
```

Give `timeout` more room than you need locally. A cold runner is slower than your machine, and a build that has not finished looks exactly like a server that will never answer.

## Step 2: add the job

.github/workflows/screenshots.yml

```
name: Screenshots
on: pull_request

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run build
      - run: npx shotlist --lint --warnings
      - run: npx shotlist --check --keep-going --diff
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: screenshot-diffs
          path: screenshots/out/diff/
```

Four flags are doing the work:

- `--lint` parses every file and reports all of them at once. It opens no browser, so it finishes in about a second and catches a misspelled key before the slow step starts. See [Check your files without a browser](/docs/how-to/lint/).
- `--check` re-takes every screenshot, compares it with the committed one, and exits non-zero if any differ.
- `--keep-going` reports every problem in one run instead of stopping at the first, so a contributor sees the whole list.
- `--diff` writes a three-panel image per changed screenshot, which the next step uploads so a reviewer can look at it without running anything.

## Step 3: report the result as data

If another job needs to read the outcome, ask for JSON:

```
npx shotlist --check --json > report.json
```

Everything written for a person moves to standard error, so the redirect leaves a usable file. The report carries a `drift` field describing any mismatch between this machine and the one that took the committed images, which lets a job tell a re-render apart from a regression without parsing prose.

## Handle pull requests from forks

A pull request from a fork can edit `shotlist.config.yaml`. Run shotlist with `--untrusted` after an operator-controlled process has started the application:

```
npx shotlist --check --untrusted --allow http://localhost:4321
```

An untrusted Project cannot start `site.serve` or approve even its own `site.url`. The operator's `--allow http://localhost:4321` grants that exact destination; the Run still refuses Project-provided destinations, paths outside the project, sessions and environment variables.

`--untrusted` confines shotlist rather than `npm ci`, a build script or the application being photographed. A fork can change all three, so run them on an isolated runner with no secrets and no access to private network services. Do not start a fork's `npm run preview` on a privileged runner and treat this flag as its sandbox. See [What a configuration can do](/docs/explanation/security-model/) for the full policy.

## Sign in without a person present

Sessions expire, so a scheduled job usually creates one at the start of the run. Name the secret's variable so the macro can read it:

```
SHOTLIST_ENV=ADMIN_PASSWORD npx shotlist --login admin --using sign-in
```

`SHOTLIST_ENV` takes a list of names, and adds to whatever `--allow-env` allowed. See [Capture a page behind a sign-in](/docs/how-to/sign-in/).

## Expect differences from a different machine

A Linux runner rasterizes text differently from a Mac, and a different Chromium version moves pixels without the site moving at all. When `--check` runs on a machine that does not match `shotlist.baseline.json`, it says so before the results.

The reliable fix is to take the committed screenshots on the same platform the job uses. See [Why screenshots drift](/docs/explanation/image-drift/).

## Related

- [Command line reference](/docs/reference/cli/)
- [Start the site automatically](/docs/how-to/start-the-site/)
- [Hide data that changes](/docs/how-to/hide-changing-data/)

Written by Nicola Mustone · Applies to shotlist 0.6.0 · Maintained by Nicola Mustone

Published date unavailable · Updated date unavailable · [View source](https://github.com/SirDarcanos/shotlist.dev/blob/main/src/pages/docs/how-to/run-in-ci.astro) · [Propose a correction](https://github.com/SirDarcanos/shotlist.dev/edit/main/src/pages/docs/how-to/run-in-ci.astro)
