Skip to main content

Manifest-Based Workflows

Manifest-based workflows are an alternative to the standard pr/merge setup that reduces S3 usage and CI time by only uploading screenshots that changed relative to the base branch.

Three workflow modes work together:

Manifest workflow sequence diagram

ModeTriggerWhat it does
manifest-generatePR pushRuns visual tests, hashes screenshots, uploads only changed images and a manifest to S3
manifest-comparePR push (after generate)3-way hash comparison against base branch; generates diffs, sets commit status, posts PR comment
manifest-mergePR mergedOverlays the PR's changeset onto the base manifest; updates base images in S3

PR Workflow

Both manifest-generate and manifest-compare run on every PR push. Generate must complete before compare runs, so the simplest setup is two sequential steps in one job.

on:
pull_request:
branches:
- main

jobs:
visual-tests:
name: Take Screenshots
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- run: npm install

# Some AWS authentication step here

- name: Generate Manifest
uses: ExpediaGroup/comparadise@v1
with:
workflow: manifest-generate
visual-test-command: npm run visual-tests
bucket-name: visual-regression-bucket
commit-hash: ${{ github.event.pull_request.head.sha }}
comparadise-host: https://my-comparadise-url.com

- name: Compare Manifest
uses: ExpediaGroup/comparadise@v1
with:
workflow: manifest-compare
bucket-name: visual-regression-bucket
commit-hash: ${{ github.event.pull_request.head.sha }}
comparadise-host: https://my-comparadise-url.com

Differential uploads

On a pull_request trigger, manifest-generate automatically uploads only the screenshots whose hash changed since the base branch's current HEAD — it resolves the live base-branch HEAD from the event and diffs against that manifest, so no extra configuration is needed. When run outside a pull request (no base branch to diff against), it uploads all screenshots.

Matrix jobs

For monorepos running visual tests in parallel, split the packages across several manifest-generate jobs — one package per job, or several packages grouped into one job (a "chunk") — and run a single manifest-compare job once all generate jobs complete.

Pass each job's package(s) as package-paths (comma separated for a chunk). manifest-generate sorts and MD5-hashes those paths into a chunk-id and writes that job's manifest to manifests/{commit-sha}/{chunk-id}.json, so parallel jobs never overwrite one another. Manifest keys are the screenshot paths exactly as they sit on disk — in a monorepo each package's screenshots already live under a package-named subdirectory, so keys are globally unique without any prefix being added. manifest-compare automatically discovers those per-chunk manifests, squashes them into the single manifests/{commit-sha}.json, and runs the comparison against it—so the compare and merge jobs need no extra configuration.

on:
pull_request:
branches:
- main

jobs:
generate:
name: Generate Manifest (${{ matrix.package }})
strategy:
fail-fast: false
matrix:
include:
- package: packages/ui
spec: '**/packages/ui/**/*.cy.ts'
- package: packages/core
spec: '**/packages/core/**/*.cy.ts'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install
# AWS authentication
- name: Generate Manifest
uses: ExpediaGroup/comparadise@v1
with:
workflow: manifest-generate
visual-test-command: npm run visual-tests --spec="${{ matrix.spec }}"
bucket-name: visual-regression-bucket
commit-hash: ${{ github.event.pull_request.head.sha }}
package-paths: ${{ matrix.package }}
comparadise-host: https://my-comparadise-url.com

compare:
name: Compare Manifest
needs: generate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# AWS authentication
- name: Compare Manifest
uses: ExpediaGroup/comparadise@v1
with:
workflow: manifest-compare
bucket-name: visual-regression-bucket
commit-hash: ${{ github.event.pull_request.head.sha }}
comparadise-host: https://my-comparadise-url.com

Merge Workflow

When a PR merges, manifest-merge updates the base manifest and base images in S3 so future comparisons are based on the latest merged state.

Important: You must set a concurrency group with cancel-in-progress: false on this workflow. Without it, two PRs merging simultaneously can race to update overlapping base images, producing a corrupted state that future manifest-compare runs will read incorrect diffs against.

on:
pull_request:
types:
- closed
branches:
- main

concurrency:
group: manifest-merge
cancel-in-progress: false

jobs:
manifest-merge:
name: Update Manifest
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# AWS authentication
- name: Update Manifest
uses: ExpediaGroup/comparadise@v1
with:
workflow: manifest-merge
bucket-name: visual-regression-bucket

The pr-sha, merge-commit-sha, and pr-number inputs are automatically read from the pull_request event payload and do not need to be set explicitly.

Required status check

manifest-compare sets the Visual Regression commit status on the PR head SHA–the same context as the standard pr mode. Add it as a required status check in your branch protection settings to block merges until visual changes are reviewed.