Files
multica/scripts/dev.sh
Lambda e583c149ef fix(dev): make the local dev flow survive its own tooling
Three defects in the documented local flow, plus the bootstrap collapse
that keeps them from coming back.

1. `make daemon` produced a daemon that could not run any task. The chain
   ended in `go run`, and the daemon records its own executable path at
   startup to re-exec as the execution-environment helper for every task.
   `go run` deletes that temp binary when the launcher exits, so the
   daemon started, registered, heartbeat — then failed every task with
   "fork/exec .../go-build.../exe/multica: no such file or directory".
   `make daemon` now builds `server/bin/multica` and runs that (`ARGS`
   forwards any daemon subcommand), and `daemon start` refuses an
   ephemeral binary up front instead of deferring the failure.

2. `make stop` killed the daemon. `lsof -ti:PORT` matches every process
   holding a socket on the port, including CLIENTS — and the daemon holds
   a long-lived connection to the backend. Stop now goes through
   scripts/port-guard.sh, which filters to `-sTCP:LISTEN` and sends TERM
   before KILL. scripts/port-guard.test.sh pins both halves against a real
   listener and a real connected client.

3. Restarting over a running instance failed invisibly: the new process
   died on the bind, the old one kept serving, and /health answered 200
   throughout. `make dev` now preflights both ports and names the process
   that owns them, and /health reports `pid`, `started_at`, and `commit`
   so a caller can prove which process answered.

4. `make dev-bootstrap` collapses the ~12-step manual sequence into one
   command: env file, fixed verification code before first launch, DB via
   DATABASE_URL (not docker exec), port preflight, detached services,
   health verified against process identity, single-shot login, PAT,
   workspace, CLI profile, built daemon binary. It pins a durable TMPDIR
   so an agent-created environment outlives the run that created it.
   `make dev-bootstrap-stop` tears it down.

Measured on a clean checkout: 11.7s to a logged-in, daemon-attached
environment.

Co-authored-by: multica-agent <github@multica.ai>
2026-08-07 16:37:26 +08:00

79 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
# ---------- Check prerequisites ----------
missing=()
command -v node >/dev/null 2>&1 || missing+=("node")
command -v pnpm >/dev/null 2>&1 || missing+=("pnpm")
command -v go >/dev/null 2>&1 || missing+=("go")
command -v docker >/dev/null 2>&1 || missing+=("docker")
if [ ${#missing[@]} -gt 0 ]; then
echo "✗ Missing prerequisites: ${missing[*]}"
echo " Please install: Node.js v20+, pnpm v10.28+, Go v1.26+, Docker"
exit 1
fi
# ---------- Environment file ----------
if [ -f .git ]; then
# Inside a git worktree (.git is a file, not a directory)
ENV_FILE=".env.worktree"
if [ ! -f "$ENV_FILE" ]; then
echo "==> Worktree detected. Generating $ENV_FILE..."
bash scripts/init-worktree-env.sh "$ENV_FILE"
fi
else
ENV_FILE=".env"
if [ ! -f "$ENV_FILE" ]; then
echo "==> Creating $ENV_FILE from .env.example..."
cp .env.example "$ENV_FILE"
fi
fi
echo "==> Using $ENV_FILE"
set -a
# shellcheck disable=SC1090
. "$ENV_FILE"
set +a
# shellcheck disable=SC1091
. scripts/local-env.sh
# ---------- Port preflight ----------
# Without this, starting over a running instance fails silently in the worst
# possible way: the new processes die on the bind, the old ones keep serving,
# and /health answers 200 the whole time — so the "restart" looks successful
# while the previous build is still the one handling every request.
bash scripts/port-guard.sh require-free "${PORT:-8080}" backend
bash scripts/port-guard.sh require-free "${FRONTEND_PORT:-3000}" frontend
# ---------- Install dependencies ----------
if [ ! -d node_modules ]; then
echo "==> Installing dependencies..."
pnpm install
fi
# ---------- Database ----------
bash scripts/ensure-postgres.sh "$ENV_FILE"
echo "==> Running migrations..."
(cd server && go run ./cmd/migrate up)
# ---------- Start services ----------
echo ""
echo "✓ Ready. Starting services..."
echo " Backend: http://localhost:${PORT:-8080}"
echo " Frontend: http://localhost:${FRONTEND_PORT:-3000}"
echo ""
trap 'kill 0' EXIT
# Stamp the commit so /health can report which build is answering.
COMMIT="$(git rev-parse --short HEAD 2> /dev/null || echo unknown)"
(cd server && go run -ldflags "-X main.commit=${COMMIT}" ./cmd/server) &
pnpm dev:web &
wait