#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" API_PROJECT="$ROOT_DIR/BoardOil.Api/BoardOil.Api.csproj" WEB_DIR="$ROOT_DIR/BoardOil.Web" DEV_DATA_DIR="$ROOT_DIR/.data/dev" MAIN_DB_PATH="$DEV_DATA_DIR/boardoil.dev.db" get_current_branch_name() { local branch_name="" branch_name=$(git -C "$ROOT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || true) if [[ -z "$branch_name" ]]; then echo "unknown" return fi if [[ "$branch_name" == "HEAD" ]]; then local short_sha="" short_sha=$(git -C "$ROOT_DIR" rev-parse --short HEAD 2>/dev/null || true) if [[ -n "$short_sha" ]]; then echo "detached-$short_sha" return fi echo "detached" return fi echo "$branch_name" } sanitise_branch_name_for_path() { local branch_name="$1" local sanitised="" sanitised=$(printf '%s' "$branch_name" | sed -E 's#[^A-Za-z0-9._-]+#_#g; s#_+#_#g; s#^_##; s#_$##') if [[ -z "$sanitised" ]]; then sanitised="unknown" fi echo "$sanitised" } copy_sqlite_database() { local source_db_path="$1" local destination_db_path="$2" if command -v sqlite3 >/dev/null 2>&1; then sqlite3 "$source_db_path" ".backup '$destination_db_path'" return fi cp "$source_db_path" "$destination_db_path" if [[ -f "${source_db_path}-wal" ]]; then cp "${source_db_path}-wal" "${destination_db_path}-wal" fi if [[ -f "${source_db_path}-shm" ]]; then cp "${source_db_path}-shm" "${destination_db_path}-shm" fi } resolve_dev_database_path() { local db_mode="${BOARDOIL_DEV_DB_MODE:-branch}" local main_branch_name="${BOARDOIL_MAIN_BRANCH_NAME:-main}" local current_branch_name="" current_branch_name=$(get_current_branch_name) if [[ "$db_mode" == "shared" ]]; then echo "$MAIN_DB_PATH" return fi if [[ "$current_branch_name" == "$main_branch_name" ]]; then echo "$MAIN_DB_PATH" return fi local branch_dir_name="" branch_dir_name=$(sanitise_branch_name_for_path "$current_branch_name") echo "$DEV_DATA_DIR/branches/$branch_dir_name/boardoil.dev.db" } seed_branch_database_from_main_if_needed() { local target_db_path="$1" local seed_from_main="${BOARDOIL_DEV_DB_SEED_FROM_MAIN:-1}" if [[ "$target_db_path" == "$MAIN_DB_PATH" ]]; then return fi if [[ "$seed_from_main" != "1" ]]; then return fi if [[ -f "$target_db_path" ]]; then return fi if [[ ! -f "$MAIN_DB_PATH" ]]; then return fi mkdir -p "$(dirname "$target_db_path")" echo "Seeding branch database from main database snapshot..." copy_sqlite_database "$MAIN_DB_PATH" "$target_db_path" } if ! command -v dotnet >/dev/null 2>&1; then echo "Error: dotnet is required but not found on PATH." >&2 exit 1 fi if ! command -v npm >/dev/null 2>&1; then echo "Error: npm is required but not found on PATH." >&2 exit 1 fi if [[ ! -d "$WEB_DIR/node_modules" ]]; then echo "Error: $WEB_DIR/node_modules is missing. Run 'cd BoardOil.Web && npm ci' first." >&2 exit 1 fi api_pid="" web_pid="" stop_stale_vite() { local port=5173 if ! command -v ss >/dev/null 2>&1; then return fi local ss_output="" ss_output=$(ss -ltnp 2>/dev/null || true) if [[ -z "$ss_output" ]]; then return fi local pids="" pids=$(awk -v port=":$port" ' $0 ~ port { while (match($0, /pid=[0-9]+/)) { pid=substr($0, RSTART+4, RLENGTH-4) print pid $0=substr($0, RSTART+RLENGTH) } } ' <<<"$ss_output" | sort -u) if [[ -z "$pids" ]]; then return fi while IFS= read -r pid; do if [[ -z "$pid" ]] || ! kill -0 "$pid" 2>/dev/null; then continue fi local args="" args=$(ps -p "$pid" -o args= 2>/dev/null || true) if [[ -z "$args" ]]; then continue fi if grep -qi "vite" <<<"$args"; then echo "Stopping existing Vite process on port $port (pid $pid)..." kill "$pid" 2>/dev/null || true fi done <<<"$pids" } cleanup() { trap - INT TERM EXIT if [[ -n "$api_pid" ]] && kill -0 "$api_pid" 2>/dev/null; then kill "$api_pid" 2>/dev/null || true fi if [[ -n "$web_pid" ]] && kill -0 "$web_pid" 2>/dev/null; then kill "$web_pid" 2>/dev/null || true fi wait "$api_pid" "$web_pid" 2>/dev/null || true } trap cleanup INT TERM EXIT echo "Starting API on http://127.0.0.1:5000 ..." mkdir -p "$DEV_DATA_DIR" DEV_DB_PATH="$(resolve_dev_database_path)" mkdir -p "$(dirname "$DEV_DB_PATH")" seed_branch_database_from_main_if_needed "$DEV_DB_PATH" echo "Using development database: $DEV_DB_PATH" echo "Building API ..." dotnet build "$API_PROJECT" -maxcpucount:1 -nodeReuse:false ASPNETCORE_ENVIRONMENT=Development \ DOTNET_ENVIRONMENT=Development \ ConnectionStrings__BoardOil="Data Source=$DEV_DB_PATH" \ dotnet run --no-launch-profile --no-build --project "$API_PROJECT" --urls http://127.0.0.1:5000 & api_pid=$! echo "Starting frontend on http://localhost:5173 ..." stop_stale_vite ( cd "$WEB_DIR" npm run dev ) & web_pid=$! wait -n "$api_pid" "$web_pid"