#!/usr/bin/env bash set -euo pipefail export MSYS_NO_PATHCONV=1 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" ROOT_ENV_FILE="$ROOT_DIR/.env" ROOT_ENV_EXAMPLE_FILE="$ROOT_DIR/.env.example" VENV_DIR="$SCRIPT_DIR/.venv" MODE="${1:-start}" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } if [ ! -f "$ROOT_ENV_FILE" ]; then if [ -f "$ROOT_ENV_EXAMPLE_FILE" ]; then warn "Root .env not found. Creating it from .env.example" cp "$ROOT_ENV_EXAMPLE_FILE" "$ROOT_ENV_FILE" else error "Root .env and .env.example are both missing." fi fi set -a . "$ROOT_ENV_FILE" set +a SERVER_HOST="${SERVER_HOST:-127.0.0.1}" SERVER_PORT="${SERVER_PORT:-8000}" SERVER_RELOAD="${SERVER_RELOAD:-false}" is_wsl() { grep -qi microsoft /proc/version 2>/dev/null } is_msys() { case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) return 0 ;; *) return 1 ;; esac } needs_windows_python() { is_msys || is_wsl } find_unix_python() { if command -v python3 >/dev/null 2>&1; then echo "python3" return 0 fi if command -v python >/dev/null 2>&1; then echo "python" return 0 fi return 1 } find_windows_python() { if command -v py.exe >/dev/null 2>&1; then echo "py.exe -3" return 0 fi if command -v python.exe >/dev/null 2>&1; then echo "python.exe" return 0 fi return 1 } venv_python_path() { if [ -x "$VENV_DIR/Scripts/python.exe" ]; then echo "$VENV_DIR/Scripts/python.exe" return 0 fi if [ -x "$VENV_DIR/bin/python" ]; then echo "$VENV_DIR/bin/python" return 0 fi return 1 } run_bootstrap_python() { $PYTHON_BOOTSTRAP "$@" } dependencies_ready() { "$PYTHON_BIN" -c "import fastapi, uvicorn, sqlalchemy, alembic, pydantic_settings" >/dev/null 2>&1 } pip_ready() { "$PYTHON_BIN" -m pip --version >/dev/null 2>&1 } create_venv() { info "Creating virtual environment at ./server/.venv" if [ -d "$VENV_DIR" ]; then rm -rf "$VENV_DIR" fi run_bootstrap_python -m venv "$VENV_DIR" if ! PYTHON_BIN="$(venv_python_path)"; then error "Virtual environment was not created successfully." fi } ensure_pip() { if pip_ready; then return 0 fi warn "pip is missing in .venv, attempting repair" if "$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 && pip_ready; then info "pip restored successfully" return 0 fi warn "Recreating .venv because pip repair failed" rm -rf "$VENV_DIR" create_venv if "$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 && pip_ready; then info "pip restored after recreating .venv" return 0 fi error "pip could not be created inside .venv. Install Python with venv and pip support, then rerun the script." } ensure_python_bootstrap() { if needs_windows_python; then if find_windows_python >/dev/null 2>&1; then PYTHON_BOOTSTRAP="$(find_windows_python)" info "Detected Windows bash environment — using Windows Python" return 0 fi if find_unix_python >/dev/null 2>&1; then PYTHON_BOOTSTRAP="$(find_unix_python)" warn "Windows Python not found, falling back to system Python" return 0 fi error "Python is not available in PATH." fi if ! PYTHON_BOOTSTRAP="$(find_unix_python)"; then error "Python is not installed or not available in PATH. Install Python 3.11+ first so the script can create server/.venv automatically." fi } ensure_dependencies() { ensure_python_bootstrap if ! PYTHON_BIN="$(venv_python_path)"; then warn "Python virtual environment not found" create_venv fi ensure_pip if dependencies_ready; then info "Server dependencies are ready." return 0 fi warn "Server dependencies are missing or incomplete" info "Running .venv Python dependency installation" "$PYTHON_BIN" -m pip install --upgrade pip "$PYTHON_BIN" -m pip install -e ".[dev]" if ! dependencies_ready; then error "Server dependencies are still incomplete after installation." fi info "Server dependencies are ready." } start_server() { info "Starting FastAPI server..." info "Access: http://$SERVER_HOST:$SERVER_PORT" echo "" if [ "$SERVER_RELOAD" = "true" ]; then exec "$PYTHON_BIN" -m uvicorn app.main:app --reload --app-dir src --host "$SERVER_HOST" --port "$SERVER_PORT" fi exec "$PYTHON_BIN" -m uvicorn app.main:app --app-dir src --host "$SERVER_HOST" --port "$SERVER_PORT" } case "$MODE" in deps) ensure_dependencies ;; start) ensure_dependencies start_server ;; *) error "Unknown mode: $MODE. Use one of: deps, start" ;; esac