| name | paparazzi-github-actions | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| description | Set up Paparazzi screenshot testing in GitHub Actions CI, with snapshot images stored in Git LFS so they don't bloat the repo. Use this whenever a user wants to run Paparazzi screenshot tests on CI, get a failure report as an artifact, move existing snapshot PNGs into Git LFS, or wire up a GitHub Actions workflow for verifying screenshots — even if they only mention "screenshot tests on CI", "Paparazzi on GitHub", or "stop my snapshots bloating the repo". | |||||||||||
| metadata |
|
This skill wires up an existing Paparazzi setup to run on GitHub Actions, stores snapshot images in Git LFS to keep the repository small, and uploads a Paparazzi report as a CI artifact when a screenshot test fails.
Git LFS matters here because a real app accumulates hundreds or thousands of snapshot PNGs. Committing them as normal git blobs bloats clone size and hits per-push and per-repo size limits. LFS stores the binaries on a separate server and keeps only small text pointers in history.
Verify all three before doing anything. If any fail, stop and tell the user what's missing.
-
Paparazzi Gradle plugin is applied. Confirm the plugin is declared in the relevant module's
build.gradle(.kts). Check the latest version at https://cashapp.github.io/paparazzi/changelog/ and suggest updating if it's old. -
Git LFS is installed on the machine. Run
git lfs versionto check. If it's missing, ask the user for permission before installing. Then:- macOS:
brew install git-lfs - Debian/Ubuntu:
sudo apt-get install git-lfs - Other: see https://git-lfs.com/
- macOS:
-
Paparazzi tests and their generated images already exist. Search the test sources for
paparazzi.snapshot {to find the tests. For each test, confirm the matching image exists underapp/src/test/snapshots/images. Snapshot filenames end with the test method name. For example:@Test fun testListItemWithLongNameFollowing() { paparazzi.snapshot { ListItemWithLongNameFollowing() } }
…must have a corresponding file ending in
..._testListItemWithLongNameFollowing.pnginapp/src/test/snapshots/images.If tests or images are missing, abort and ask the user whether they want screenshot tests created first.
Workflows live in .github/workflows/ (note the leading dot). If that directory
or a workflow file doesn't exist, create .github/workflows/screenshot-tests.yml
using the reference workflow below, then go to Step 3.
If a workflow file already exists, do not overwrite it — go to Step 2 instead.
Reference workflow:
name: Screenshot Tests
on:
push:
branches: [ main ]
pull_request:
jobs:
paparazzi:
runs-on: ubuntu-latest
steps:
# Checks out the repo. lfs: true pulls the real PNGs, not just LFS pointers.
- name: Checkout Repo
uses: actions/checkout@v4
with:
lfs: true
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Verify Paparazzi screenshots
run: ./gradlew :app:verifyPaparazziDebug
# Paparazzi only emits a report on failure; upload it so the diff is inspectable.
- name: Upload Paparazzi failure report
if: failure()
uses: actions/upload-artifact@v4
with:
name: paparazzi-report
path: app/build/reports/tests/testDebugUnitTest/
if-no-files-found: ignoreAdjust :app:verifyPaparazziDebug and the report path to match the user's module
name and build variant (e.g. :feature-profile:verifyPaparazziRelease →
feature-profile/build/reports/tests/testReleaseUnitTest/).
When a workflow already exists, make two edits to the job that runs the tests:
-
Add
lfs: trueto the checkout step so CI pulls the actual images instead of LFS pointers (without this, every verify will fail):- name: Checkout Repo uses: actions/checkout@v4 with: lfs: true
-
Ensure the workflow runs
./gradlew :app:verifyPaparazziDebugand uploads the report on failure. If the verify and upload-artifact steps from the reference workflow are absent, add them (adjusting module/variant names).
Run from the repository root:
# One-time init of LFS hooks for this user
git lfs install
# Track every PNG under any snapshots directory
git lfs track "**/snapshots/**/*.png"
# Commit the tracking config
git add .gitattributes
# Optional: avoids LFS marking files read-only, which speeds up checkouts
git config lfs.setlockablereadonly falsegit lfs track only affects files committed after this point — it does not
retroactively move already-committed images into LFS. That's handled in Step 4.
Determine whether the snapshot PNGs are already committed to git history:
git log --oneline -- "**/snapshots/**/*.png" | head -1Case A — images already committed (the common case). Rewrite history to move them into LFS:
git lfs migrate import --include="**/snapshots/**/*.png"This rewrites commits, so it changes commit hashes. On a shared branch, coordinate
with the team and expect a force-push (git push --force-with-lease). Add
--everything to migrate across all branches and tags rather than just the current
branch. Warn the user about the history rewrite before running this.
Case B — images present on disk but never committed. Just stage and commit them
normally; git lfs track from Step 3 routes them into LFS automatically:
git add .
git commit -m "Track Paparazzi snapshots with Git LFS"Confirm the images are now LFS-tracked rather than stored inline:
git lfs ls-filesEach snapshot PNG should appear in the list. To double-check a single file shows a pointer (three lines: version, oid, size) instead of binary content:
git show HEAD:app/src/test/snapshots/images/<some_snapshot>.pngThen push (git push, or git push --force-with-lease if Step 4 Case A rewrote
history). On push you should see LFS upload progress, e.g.
Uploading LFS objects: 100% (6/6), 162 KB | 0 B/s, done.
Push the branch and open/update a PR so the workflow runs. On the first green run,
the setup is complete: CI checks out real images via LFS, runs
verifyPaparazziDebug, and uploads a downloadable HTML report only when a
screenshot regression is detected.
- GitHub-hosted Ubuntu runners ship with Git LFS preinstalled, so no install step
is needed in the workflow — only
lfs: trueon checkout. - Paparazzi produces a report only on failure. A passing run uploads nothing, which is expected.
- The failure report is an
index.htmlplus assets under the test report path; the uploaded artifact is a zip the user downloads and opens locally.