Make Setup Command Design
Date: 2026-05-10
Status: Approved in brainstorming, ready for planning
1) Goal and scope
Add a root make setup command that prepares a newly created git worktree for local development, so the workflow becomes: create worktree, run make setup, then start working.
In scope:
- Add a root
make setuptarget. - Bootstrap JS/TS dependencies with Bun.
- Create missing local env files from tracked examples.
- Start required local infrastructure with Docker Compose.
- Wait for infrastructure readiness before backend database work.
- Run backend migrations.
- Seed demo data only when the target local database is empty.
- Update onboarding docs to make
make setupthe primary worktree bootstrap path.
Out of scope:
- Installing system prerequisites such as Bun or Docker.
- Global machine provisioning beyond this repository.
- Mobile app onboarding.
- Firmware onboarding.
- Production deployment behavior.
2) User contract
make setup is a strict, fail-fast worktree bootstrap command.
Expected behavior:
- Validate Docker CLI availability.
- Validate Docker daemon availability.
- Run
bun installat repo root. - Create
backend/.envfrombackend/.env.exampleonly ifbackend/.envdoes not already exist. - Create
frontend/.envfromfrontend/.env.exampleonly iffrontend/.envdoes not already exist. - Start infra via
docker compose -f docker-compose.infra.yml up -d. - Wait until required services are healthy enough for migrations and seed.
- Run
bun run migrate. - Run demo seed only if the local database is empty.
- Print the next step:
make dev.
Failure policy:
- If Docker is unavailable, exit immediately with a clear error.
- If infra does not become ready within a bounded wait window, exit immediately with a clear error.
- If migrations fail, do not continue to seed.
- If the database-empty check fails, exit non-zero and surface the failing step.
- If conditional seeding runs and fails, exit non-zero and surface the failing step.
3) Recommended implementation
Use a thin Makefile target that delegates orchestration to a shell script.
3.1 Makefile
Update the root Makefile to:
- add
setupto.PHONY - add a
setup:target that runsbash ./scripts/setup.sh
Reasoning:
- The existing
Makefilealready acts as a command surface. - Setup orchestration needs branching, validation, and readiness loops that are harder to maintain inline.
- A dedicated script keeps the
Makefilereadable and reduces risk of quoting mistakes.
3.2 Setup script
Create scripts/setup.sh as the single owner of onboarding orchestration.
Script requirements:
- use strict shell options:
set -euo pipefail - emit clear stage labels for each onboarding step
- keep logic idempotent where reasonable
- stop on first failure
Planned structure:
check_docker_clicheck_docker_daemoninstall_dependenciesensure_env_filesstart_infrawait_for_infrarun_migrationsseed_demo_data_if_database_emptyprint_success_message
This can be implemented with shell functions in one file. No additional runtime or helper package is needed.
4) Environment file policy
The setup command should not overwrite an existing local env file.
Rules:
- If
backend/.envis missing, copy frombackend/.env.example. - If
frontend/.envis missing, copy fromfrontend/.env.example. - If either file already exists, leave it unchanged and print that it was preserved.
Rationale:
- A new worktree gets sensible defaults automatically.
- An existing worktree does not lose local customization by re-running
make setup.
5) Infrastructure readiness
The script must not rely on docker compose up -d alone before running migrations.
Readiness design:
- Start the existing infra stack from
docker-compose.infra.yml. - Wait for the
postgresservice to report healthy before running migrations. - Optionally verify other services are started or healthy for clearer onboarding diagnostics, but only PostgreSQL readiness is a hard requirement for migration and seed.
Recommended policy:
- Treat
postgres,redis,emqx, andminiostartup as part of onboarding success messaging. - Gate database migration/seed on PostgreSQL health.
- Always run
docker compose ... up -d, even if shared local infra is already running, because that is idempotent and keeps setup behavior simple across worktrees.
Recommended wait behavior:
- bounded polling loop with a clear timeout
- print current wait status while polling
- on timeout, print a suggestion to inspect
docker compose -f docker-compose.infra.yml ps
6) Seed behavior and idempotency
make setup should not seed unconditionally. It should seed only when the target local database is empty.
Design constraint:
- Multiple worktrees may share the same local PostgreSQL instance and the same local development database.
- Re-running setup in a second worktree must not duplicate demo data or fail simply because another worktree has already initialized the database.
Required handling:
- Before running
bun run seed:demo, detect whether the local database is empty. - If empty, run the demo seed.
- If not empty, skip the seed and print that setup detected an existing initialized database.
Preferred direction:
- Use a simple, explicit database-emptiness check rather than relying on duplicate-key failures from the seed path.
- The emptiness check should be based on a stable application-level signal, such as presence of rows in a core table populated by the demo seed, rather than Docker/container state.
Implementation note:
- The exact sentinel query should be chosen after inspecting the current seed path and schema.
- If there is no reliable sentinel yet, implementation may add one, but the command contract remains: seed only when the database is empty.
7) Output and operator guidance
The command should be explicit about what it is doing.
Minimum output:
- stage start message
- success or failure message per major stage
- final success summary with:
- env files status
- infra started
- migrations applied
- demo data seeded or skipped
- next command:
make dev
The script should avoid noisy low-signal output beyond what underlying tools already print.
8) Testing strategy
This change mixes shell orchestration and existing app commands, so testing should be split between targeted behavior checks and command-level verification.
8.1 TDD expectations
No production behavior change should be added without first defining the failing verification for that behavior.
Likely sequence:
- Define the missing worktree bootstrap behavior in documentation or verification steps.
- Add any focused tests needed for extracted logic if implementation introduces testable helpers in code.
- Run command-level verification for the shell flow.
8.2 Verification targets
Implementation should verify at least:
make setupexists and is discoverable in the rootMakefile- env-file creation preserves existing files
- Docker unavailability produces a clear failure
- setup can complete successfully in a valid local environment
- setup skips seed when the shared local database is already initialized
- onboarding docs reflect the new primary worktree flow
8.3 Repo-level verification
After implementation:
- run any targeted tests introduced for this work
- run
bun run check
If full make setup execution cannot be validated in the current environment, report that explicitly.
9) Documentation changes
Update README.md so the primary onboarding flow becomes:
make setup
make dev
This section should be framed around a new git worktree, not just a first clone.
Manual fallback details can remain below that section:
- Bun install
- env-file copy
- migration command
The Make Commands table should also include make setup.
10) Risks and mitigations
Risk: setup becomes flaky because migrations run before PostgreSQL is ready.
Mitigation: explicit readiness polling on PostgreSQL health.
Risk: rerunning setup duplicates seed data or fails with uniqueness errors.
Mitigation: gate seed execution on an explicit database-empty check.
Risk: overwriting local env files breaks existing developers.
Mitigation: copy env files only when missing.
Risk: inline shell in Makefile becomes hard to maintain.
Mitigation: keep orchestration in scripts/setup.sh.
Risk: the chosen empty-database sentinel is too weak and skips seed incorrectly.
Mitigation: base the check on a stable seeded application table and verify it against the current demo seed behavior.
11) Open implementation decisions already resolved
The following decisions are fixed for implementation:
make setupis the primary worktree bootstrap path.- Docker is required and absence is a hard failure.
- The command must always run
bun install. - The command must always run
docker compose -f docker-compose.infra.yml up -d. - The command should prepare demo data only when the local database is empty.
- The
Makefileshould stay thin and delegate to a script.