§01|Preview branches

seed every supabase preview branch.


Supabase preview branches ship with an empty database. Run satus once after the branch is created and every PR review gets a fully seeded environment. The 10,000-row safety guard is harmless here: preview branches start at zero.

# pull the postgres url from the supabase CLI
$ export DATABASE_URL=$(supabase --experimental branches get $BRANCH --output json | jq -r .POSTGRES_URL)
$ export OPENAI_API_KEY=$OPENAI_API_KEY
# seed it. one transaction. all-or-nothing.
$ satus generate --profile ecommerce --rows 50
✓ inserted 250 rows across 5 tables
note

Each run generates fresh data—the model varies values by design. If a review depends on stable fixtures, keep the branch alive instead of re-seeding it, or snapshot the seeded state with pg_dump.

§02|GitHub Actions

one job. one step. one secret per env.


The most common shape. Plan in PR jobs (no writes, free), generate on merge to main or against ephemeral DBs.

# .github/workflows/seed.yml
$ name: seed
$ on: { pull_request: {}, push: { branches: [main] } }
$ jobs:
$ seed:
$ runs-on: ubuntu-latest
$ env:
$ DATABASE_URL: ${{ secrets.DATABASE_URL }}
$ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
$ steps:
$ - uses: actions/checkout@v4
$ - uses: actions/setup-node@v4
$ with: { node-version: 20 }
$ - run: npm i -g @passkeybridge/satus
$ - run: satus generate --profile saas --dry-run --json
$ if: github.event_name == 'pull_request'
$ - run: satus generate --profile saas
$ if: github.ref == 'refs/heads/main'
note

satus generate --dry-run --json introspects but never writes and never calls the model—no LLM key needed, no spend. It exits non-zero when the relational validator finds errors, so it works as a PR gate for schema-breaking changes.

§03|E2E reset

fresh data between every test suite.


Cypress, Playwright, and Vitest E2E suites need a known-good database state. The pattern: seed once with --truncate, snapshot the result with pg_dump, and restore that fixture between suites— restores are fast and byte-identical, which generation is not.

# scripts/reset-test-db.sh
$ #!/usr/bin/env bash
$ set -euo pipefail
# truncate the target tables and re-seed in one run
$ satus generate --profile ecommerce --truncate
note

--truncate issues TRUNCATE ... RESTART IDENTITY on the run’s tables inside the same transaction as the inserts, so sequences reset and a failed run leaves the old data in place. Note the absent CASCADE: if a table outside the run set has a foreign key into one inside it, satus stops with an error rather than emptying a table you never asked it to seed. Bring that table into the run set or truncate it yourself. Wire this into Cypress’s before(...) hook or Playwright’s globalSetup.

§04|Neon branching

seed a fresh neon branch in one shell.


Neon's copy-on-write branches make per-developer or per-PR databases cheap. Combine that with satus and every engineer gets their own seeded database for the cost of the branch metadata.

# create a branch and capture its connection string
$ BRANCH=$(neon branches create --name pr-$PR --project-id $NEON_PROJECT_ID --output json)
$ export DATABASE_URL=$(echo "$BRANCH" | jq -r .connection_uris[0].connection_uri)
# seed
$ satus generate --profile saas
note

Tear the branch down on PR close. Neon charges per active branch—a stale fleet of seeded branches will surprise the bill.

Have a recipe you want documented? Email support@satus.sh. We add the most-requested integrations first.