Direct `docker compose -f infra/local/dev.compose.yml ...` works
fine, but two things kept biting on this stack:
- Compose-profile asymmetry — `down` only operates on services
whose profile is currently active, so anything brought up with
`--profile X` keeps running unless the same flag is on `down`.
pgweb and Jaeger silently survived several `down -v` invocations
before we spotted it.
- Verbose invocations — typing the compose-file flag and the
profile flags on every command for routine ops gets old fast.
Add `infra/local/dev.sh` as a thin wrapper:
./infra/local/dev.sh up # core only
./infra/local/dev.sh up all # core + every profile
./infra/local/dev.sh up dbtools # core + pgweb
./infra/local/dev.sh up observability # core + Jaeger
./infra/local/dev.sh down [-v] # always with all profiles
./infra/local/dev.sh stop <service> # stop one service
./infra/local/dev.sh restart <service> # restart one service
./infra/local/dev.sh status # ps with all profiles
./infra/local/dev.sh logs [service] # follow logs
./infra/local/dev.sh exec <svc> <cmd> # run inside a container
Anything not matching one of the named verbs is passed through to
`docker compose -f dev.compose.yml ...` (with every profile flagged
in), so the full Compose surface remains available without losing
the profile-symmetry guarantee.
Docs:
- `infra/README.md` "Local-dev stack" — new "Convenience script"
subsection with the cheat-sheet table; "First-time setup" walk-
through rewritten to use the script; the previous standalone
"Profiles must match on `down` as on `up`" tip is collapsed
into a one-liner since the script handles it.
- `docs/development.md` §3 — point at the script for the typical
setup flow.
The compose file itself is unchanged.