Skip to content

Check snapshots

A check target runs a development tool that reports on the shape of your application — routes, scheduled jobs, database schema, test results, static analysis, code style — and snapshots the normalized result. Drift then shows up as a semantic diff naming exactly what changed:

~ $.data.GET|HEAD /users: {"middleware":["web","auth"],…} → undefined
~ $.data.users.nickname: undefined → "varchar"
~ $.data.Tests\Unit\BillingTest.refunds are idempotent: {"status":"passed"} → {"status":"failed",…}

Every parser normalizes its tool's output into a keyed record — entries keyed by identity (route, table, test name) rather than array position — so a removed route diffs as one named entry, not an index shift. Run-varying noise (timings, next-run dates, absolute paths, line numbers) is masked at capture time, and tools that emit different output shapes in different environments (several do, depending on who runs them) normalize to one snapshot, so a baseline captured in CI compares clean against a capture from anywhere else.

Configure a target

json
{ "kind": "check", "name": "routes", "tool": "laravel-routes" }

Options:

  • tool (required) — which parser to use (see the table below).
  • command — override the tool's default command.
  • output — read an existing report file instead of capturing stdout. Set both command and output to run a command and then read the file it writes (test runners work this way by default).
  • cwd — directory to run in, relative to the project root.
  • timeoutMs — command timeout (default: the lifecycle wait timeout).

Tools whose non-zero exit means "findings exist" (test runners, static analysis) don't fail the capture — the findings are the snapshot. The capture fails only when the tool produces no usable output.

Tools

toolDefault commandSnapshots
laravel-routesphp artisan route:list --jsonroute → name, action, middleware
laravel-aboutphp artisan about --jsonenvironment, cache, driver sections
laravel-schedulephp artisan schedule:list --jsoncron entry → command, timezone, mutex
laravel-schemaphp artisan schema:dump (reads the dump file)table → columns, indexes, constraints; migration names
pest / phpunitvendor/bin/pest --log-junit …suite → test → status (+ failure message)
phpstanvendor/bin/phpstan analyse --error-format=json --no-progressfile → message → identifier, count
pintvendor/bin/pint --test --format=json -vfile → the style rules it violates

Per-tool notes:

  • laravel-routes — keyed "GET|HEAD /users". Middleware changes and removed routes are the headline risky changes. Source file is kept; its line number is dropped (it churns on unrelated edits).
  • laravel-about — version fields are kept on purpose (that drift is signal); absolute project paths are replaced with <project> so snapshots are machine-portable. The views cache flag is dropped — running the test suite compiles Blade views as a side effect, which would flake this target.
  • laravel-schedule — needs Laravel ≥ 12 for --json. next_due_date fields are dropped (they vary with the wall clock); a cron expression change reads as remove + add, deliberately loud.
  • laravel-schema — the default reads database/schema/sqlite-schema.sql; on another connection set output to its dump path. Migration names are snapshotted, ids/batches are not.
  • pest / phpunit — one JUnit normalizer covers both runners. Timing and assertion counts are dropped; failure messages keep the assertion text with project paths relativized.
  • phpstan — Larastan included (same binary and output). Keyed by message per file with a count — the same identity PHPStan's own baseline uses — so line-number churn never dirties a snapshot.
  • pint — snapshots each drifting file's fixer names (the style rules that would rewrite it), not diff text, so the baseline is identical whatever environment captured it.

Zero-config for Laravel

dungbeetle init detects a Laravel app via composer.json and scaffolds check targets automatically: routes, about, schedule, and schema always; tests (Pest preferred, PHPUnit fallback), style (Pint), and static-analysis (PHPStan/Larastan) when the tool is installed. dungbeetle doctor then verifies each wrapped binary exists — a missing vendor/bin/pint fails with "run composer install?" before any capture runs.

See the Laravel example for the full walkthrough.

Ingesting reports from CI

Every tool also works in ingest mode — point output at a report your CI already produces and no command runs:

json
{ "kind": "check", "name": "tests", "tool": "phpunit", "output": "reports/junit.xml" }

Source-available: CLI under FSL-1.1-ALv2, cloud server under BUSL-1.1. See Licensing.