Skip to content

CI/CD Automation

This page is not meant to be every possible CI setup. It is the shortest set of patterns that map cleanly to the current CLI.

The core pattern

Most pipelines want one of these:

  1. Run tests and wait for visual results.
  2. Upload screenshots that were generated elsewhere.
  3. Finalize a parallel build after all shards finish.

For smoke jobs that intentionally have no cloud token, run with --allow-no-token. Vizzly still runs the command locally and skips creating a cloud build.

GitHub Actions

Fail when visual changes need review

name: Visual Tests
on: [push, pull_request]
jobs:
visual-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- name: Run visual tests
env:
VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
run: vizzly run "npm test" --wait

Parallel shards

Use a shared parallel-id across shards, then finalize once at the end.

jobs:
visual-tests:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- name: Run shard
env:
VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
run: |
vizzly run "npm test -- --shard=${{ matrix.shard }}/4" \
--parallel-id "${{ github.run_id }}"
finalize:
needs: visual-tests
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- name: Finalize Vizzly build
env:
VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
run: vizzly finalize "${{ github.run_id }}"

GitLab CI

visual-tests:
stage: test
image: node:22
variables:
VIZZLY_TOKEN: $VIZZLY_TOKEN
script:
- npm ci
- vizzly run "npm test" --wait

If you do want to parse JSON in a node:22 image, install jq first or use a small Node script instead.

CircleCI

version: 2.1
jobs:
visual-test:
docker:
- image: cimg/node:22.0
steps:
- checkout
- run: npm ci
- run:
name: Run visual tests
command: vizzly run "npm test" --wait
workflows:
test:
jobs:
- visual-test

Upload-only pipelines

If your test framework already captured screenshots and you just need to upload them:

Terminal window
vizzly upload ./screenshots --wait --json

That works well when screenshot capture and Vizzly upload happen in separate steps.

Preview uploads

If you also want a static preview tied to the build:

Terminal window
npm run build
vizzly preview ./dist

vizzly preview uses the current session or an explicit build ID:

Terminal window
vizzly preview ./dist --build <build-id>

Shell script example

#!/bin/bash
result=$(vizzly run "npm test" --wait --json)
exit_code=$?
echo "$result"
if echo "$result" | jq -e '.data.uploads.failed > 0' >/dev/null; then
echo "Vizzly is missing screenshots:"
echo "$result" | jq -r '.data.uploads.failures[] | "\(.name): \(.error)"'
fi
if [ "$exit_code" -ne 0 ]; then
url=$(echo "$result" | jq -r '.data.url // empty')
echo "The Vizzly run did not pass"
[ -n "$url" ] && echo "$url"
exit "$exit_code"
fi

This script reports missing uploads without changing the test result. vizzly run preserves your test command’s exit code when uploads fail; --wait can still fail the job for visual differences after successful uploads. A zero exit code alone doesn’t confirm a complete visual build.

Comparison counts are included only when the API returned them, so scripts should not invent missing counts as zero.

Tips

  • Use Node 22 in CI to match the current CLI package requirement.
  • Use vizzly run --wait to fail on visual differences, and check upload results for missing screenshots.
  • Use vizzly finalize only when you are intentionally running parallel shards.
  • Save the JSON output if later jobs need the build ID or URL.

See also