feat(infra): add dev.sh wrapper for the local-dev compose stack (#68)
## Summary Two recurring frictions on the local-dev stack: 1. **Compose-profile asymmetry** — `docker compose down` only operates on services whose profile is currently active. Anything brought up with `--profile X` keeps running unless the same flag is passed on `down`. pgweb and Jaeger silently survived several `down -v` invocations before we noticed (#67 documented the gotcha; this PR makes it impossible to hit if you use the wrapper). 2. **Verbose invocations** — typing `docker compose -f infra/local/dev.compose.yml --profile … <verb>` for routine ops gets old fast. Add [`infra/local/dev.sh`](infra/local/dev.sh) as a thin wrapper. Always passes every profile in scope on teardown / status / log commands, exposes ergonomic verbs: | Command | Effect | | --------------------------------------------- | --------------------------------------------------------------- | | `./infra/local/dev.sh up` | Core only (postgres + redis + otel-collector) | | `./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]` | Tear down (every profile in scope, no orphaned services) | | `./infra/local/dev.sh stop <service>` | Stop one service (containers stay around) | | `./infra/local/dev.sh restart <service>` | Restart one service | | `./infra/local/dev.sh status` | `ps` with every profile visible | | `./infra/local/dev.sh logs [service]` | Follow logs | | `./infra/local/dev.sh exec <service> <cmd>` | Run a command 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. ## Doc updates - **`infra/README.md`** — new "Convenience script" subsection with the cheat-sheet table; "First-time setup" rewritten to use the script; the standalone "Profile symmetry" tip from #67 is collapsed into a one-liner since the script now handles it (the note remains as a fallback for direct `docker compose` users). - **`docs/development.md` §3** — points at the script for the typical setup flow. The compose file itself is unchanged. ## Test plan - [ ] `./infra/local/dev.sh help` prints the usage block. - [ ] `./infra/local/dev.sh up` brings up the 3 core services (no pgweb / no jaeger). - [ ] `./infra/local/dev.sh up all` adds pgweb and Jaeger. - [ ] `./infra/local/dev.sh down -v` stops and removes all 5 containers (incl. pgweb and Jaeger), wipes the postgres-data and redis-data volumes. - [ ] `./infra/local/dev.sh stop pgweb` stops just pgweb. - [ ] `./infra/local/dev.sh logs otel-collector` follows that service's logs. - [ ] `./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\du"` lists the audit roles. - [ ] `./infra/local/dev.sh stop` (no arg) errors with a clear message. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #68
This commit was merged in pull request #68.
This commit is contained in:
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
# Convenience wrapper around infra/local/dev.compose.yml. Documented
|
||||
# in infra/README.md → "Local-dev stack" → "Convenience script".
|
||||
#
|
||||
# Hides the Compose-profile gotcha: `docker compose down` only acts
|
||||
# on services whose profile is currently active, so anything brought
|
||||
# up with `--profile X` keeps running unless the same flag is on
|
||||
# `down`. Every teardown / status / log command in this script
|
||||
# always includes every profile in scope, so profile-gated services
|
||||
# are visible and stoppable.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/dev.compose.yml"
|
||||
|
||||
# Profiles defined in dev.compose.yml. Keep in sync if a new profile
|
||||
# is added.
|
||||
ALL_PROFILES=(dbtools observability)
|
||||
|
||||
# Build "--profile p1 --profile p2 …" as separate arguments.
|
||||
build_all_profile_flags() {
|
||||
local p
|
||||
ALL_PROFILE_FLAGS=()
|
||||
for p in "${ALL_PROFILES[@]}"; do
|
||||
ALL_PROFILE_FLAGS+=(--profile "$p")
|
||||
done
|
||||
}
|
||||
|
||||
# Run docker compose with every profile flagged in.
|
||||
dc_all() {
|
||||
build_all_profile_flags
|
||||
docker compose -f "$COMPOSE_FILE" "${ALL_PROFILE_FLAGS[@]}" "$@"
|
||||
}
|
||||
|
||||
# Run docker compose with no profiles (core services only).
|
||||
dc_core() {
|
||||
docker compose -f "$COMPOSE_FILE" "$@"
|
||||
}
|
||||
|
||||
# Run `up -d` with a custom set of profiles.
|
||||
up_with_profiles() {
|
||||
local p
|
||||
local -a flags=()
|
||||
for p in "$@"; do
|
||||
flags+=(--profile "$p")
|
||||
done
|
||||
docker compose -f "$COMPOSE_FILE" "${flags[@]}" up -d
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: ./infra/local/dev.sh <command> [args]
|
||||
|
||||
Commands:
|
||||
up [target...] Bring up the stack.
|
||||
Targets:
|
||||
(none) core only (postgres + redis + otel-collector)
|
||||
all core + all profiles
|
||||
dbtools core + pgweb
|
||||
observability core + jaeger
|
||||
Multiple targets allowed (e.g. `up dbtools observability`).
|
||||
|
||||
down [-v] Tear the stack down. Always runs with every
|
||||
profile in scope, so profile-gated services
|
||||
(pgweb, jaeger) are caught too. -v also wipes
|
||||
the named Docker volumes.
|
||||
|
||||
stop <service> Stop a single service (containers stay around).
|
||||
Use `up <service>` (or restart) to bring it back.
|
||||
|
||||
restart <service> Restart a single service.
|
||||
|
||||
status docker compose ps, with every profile in scope.
|
||||
|
||||
logs [service] Follow logs (one service or all of them).
|
||||
|
||||
exec <service> <cmd> Run a command inside a running service container.
|
||||
|
||||
help Show this help.
|
||||
|
||||
<other> [args...] Anything else is passed through to
|
||||
`docker compose -f dev.compose.yml ...` with
|
||||
every profile in scope.
|
||||
|
||||
Examples:
|
||||
./infra/local/dev.sh up
|
||||
./infra/local/dev.sh up all
|
||||
./infra/local/dev.sh up observability
|
||||
./infra/local/dev.sh down -v
|
||||
./infra/local/dev.sh stop pgweb
|
||||
./infra/local/dev.sh logs otel-collector
|
||||
./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
USAGE
|
||||
}
|
||||
|
||||
require_args() {
|
||||
local verb="$1"
|
||||
local n="$2"
|
||||
local hint="$3"
|
||||
if [[ $# -lt $((3 + n)) ]]; then
|
||||
echo "Error: '$verb' requires $hint." >&2
|
||||
echo "Try: ./infra/local/dev.sh help" >&2
|
||||
exit 64
|
||||
fi
|
||||
}
|
||||
|
||||
cmd="${1:-help}"
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
case "$cmd" in
|
||||
up | start)
|
||||
if [[ $# -eq 0 ]]; then
|
||||
dc_core up -d
|
||||
elif [[ "$1" == "all" ]]; then
|
||||
dc_all up -d
|
||||
else
|
||||
up_with_profiles "$@"
|
||||
fi
|
||||
;;
|
||||
down)
|
||||
dc_all down "$@"
|
||||
;;
|
||||
stop)
|
||||
require_args stop 1 "a service name (use 'down' to tear down the whole stack)" "$@"
|
||||
dc_all stop "$@"
|
||||
;;
|
||||
restart)
|
||||
require_args restart 1 "a service name" "$@"
|
||||
dc_all restart "$@"
|
||||
;;
|
||||
status | ps)
|
||||
dc_all ps "$@"
|
||||
;;
|
||||
logs)
|
||||
dc_all logs -f "$@"
|
||||
;;
|
||||
exec)
|
||||
require_args exec 2 "a service name and a command" "$@"
|
||||
dc_all exec "$@"
|
||||
;;
|
||||
help | -h | --help | "")
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
# Pass-through to docker compose, with profiles in scope.
|
||||
dc_all "$cmd" "$@"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user