Thanks for your interest in improving snapdiff. This guide covers how to set
up a dev environment, build and test the project, and how design decisions are
recorded.
Before diving in, skim docs/spec.md — it's the canonical
system design (V-Model, scope, architecture) and the fastest way to understand
how the pieces fit together.
-
Go 1.26.3+ (the version is pinned in
go.mod). -
templ— the web views are generated from.templfiles, somake buildrunsmake generatefirst. Install it once:GOBIN=$HOME/go/bin go install github.com/a-h/templ/cmd/templ@latestThe Makefile looks for
templat$HOME/go/bin/templ; override withmake TEMPL=/path/to/templ ...if yours lives elsewhere. -
Node 22+ and pnpm 8.14.1 — optional, only needed to run the Playwright screenshot suite (see Screenshot dogfood).
cmd/snapdiff/ CLI entry point (await / serve / gallery / version)
internal/
config/ snapdiff.toml parsing & validation
gitscan/ git diff/show wrappers + axis extraction
imdiff/ PNG pixel-diff
review/ in-memory session & verdict state
web/ chi router + templ views
apply/ applies verdicts (git checkout / remove)
lifecycle/ daemon startup & graceful shutdown
gallery/ read-only baseline browser
test/
acceptance/ end-to-end tests (-tags acceptance)
integration/ package-level integration tests
fixtures/ sample git repos & images
tests-screenshots/ Playwright visual-regression suite (opt-in)
docs/
spec.md system design (V-Model)
v-model.md verification framework & traceability
adr/ architecture decision records
make build # generates templ output, then builds ./snapdiff
./snapdiff serve
make install # build, then copy to /usr/local/bin/snapdiff (uses sudo)The version string is derived from git describe --tags --always --dirty and
baked in at build time, so local builds report something like v0.0.1-3-gabc123.
| Target | What it does |
|---|---|
all |
lint test build |
generate |
Regenerate Go from .templ views (requires templ). |
build |
Generate, then build ./snapdiff. |
install |
Build, then copy the binary to /usr/local/bin (sudo). |
test |
go test ./... (unit + integration). |
acceptance |
End-to-end tests: go test -tags acceptance ./test/acceptance/.... |
vet |
go vet ./.... |
fmt |
gofmt -w .. |
lint |
gofmt check (excludes *_templ.go) + go vet. |
clean |
Remove ./snapdiff and dist/. |
release |
Cross-compile the 5-platform matrix into dist/. |
screenshots-install |
pnpm install + install the chromium browser. |
screenshots |
Run the Playwright suite. |
screenshots-update |
Rewrite the screenshot baselines. |
snapdiff follows a V-Model: tests exist at several levels, and the acceptance
test is the north star. The everyday checks are:
make lint # gofmt + go vet
make test # unit + integration
make acceptance # end-to-end against a fixture repoCI runs all three on every push to main and every pull request, so run them
locally before opening a PR. See docs/v-model.md for the
requirement-to-test traceability matrix.
The web UI is covered by a Playwright suite under tests-screenshots/. Per
ADR-0017, the baselines are reviewed with snapdiff itself —
there's a snapdiff.toml at the repo root pointing at
tests-screenshots/baselines/.
First-time setup:
make screenshots-install # pnpm install + chromiumWhen you change CSS or templ views:
make screenshots # green if no regression
# if it fails and the change is intended:
make screenshots-update # rewrite baselines
snapdiff serve # review your own diff in snapdiff
# approve / reject in the browser; rejected files are reverted via
# `git checkout HEAD --`, approved ones stay dirty for `git commit`.Architectural decisions are recorded as ADRs in docs/adr/. If
you're proposing something that changes the architecture or a documented
trade-off (git-as-state, no-auth, config format, etc.), add an ADR using
docs/adr/template.md as part of your PR. Read the
existing ADRs first — many "why isn't this configurable / persisted / pluggable"
questions are already answered there.
Use Conventional Commits (feat:,
fix:, docs:, test:, chore:, …). The release changelog is generated by
GoReleaser, which excludes docs:, test:, chore:, and merge commits — so
keep user-facing changes under feat:/fix:.
Releases are automated. A maintainer pushes a v* tag; GitHub Actions runs the
full lint + test suite and then GoReleaser cross-compiles the matrix (darwin,
linux, windows × amd64/arm64, minus windows/arm64) and publishes a GitHub
Release with archives and checksums.txt.