Files
paragliding/dev.sh
2026-01-03 14:16:16 -08:00

448 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# dev.sh - Idempotent local development setup script
# This script can be run multiple times safely
#
# Usage:
# ./dev.sh - Setup only (PostgreSQL + .env files + migrations)
# ./dev.sh --start - Setup and start backend + frontend
# ./dev.sh --stop - Stop running services
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
DB_URL="postgres://dev:devpass@localhost:5432/paragliding?sslmode=disable"
CONTAINER_NAME="paragliding-postgres"
BACKEND_DIR="backend"
FRONTEND_DIR="frontend"
BACKEND_PID_FILE=".backend.pid"
FRONTEND_PID_FILE=".frontend.pid"
BACKEND_LOG_FILE="backend.log"
FRONTEND_LOG_FILE="frontend.log"
# Helper functions
info() {
echo -e "${BLUE}${NC} $1"
}
success() {
echo -e "${GREEN}${NC} $1"
}
warning() {
echo -e "${YELLOW}${NC} $1"
}
error() {
echo -e "${RED}${NC} $1"
}
# Check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if PostgreSQL container is running
is_postgres_running() {
if command_exists podman; then
podman ps --filter "name=$CONTAINER_NAME" --filter "status=running" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"
elif command_exists docker; then
docker ps --filter "name=$CONTAINER_NAME" --filter "status=running" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"
else
return 1
fi
}
# Check if PostgreSQL is ready to accept connections
is_postgres_ready() {
if command_exists podman; then
podman exec "$CONTAINER_NAME" pg_isready -U dev -d paragliding >/dev/null 2>&1
elif command_exists docker; then
docker exec "$CONTAINER_NAME" pg_isready -U dev -d paragliding >/dev/null 2>&1
else
return 1
fi
}
# Start PostgreSQL if not running
start_postgres() {
if is_postgres_running; then
success "PostgreSQL is already running"
return 0
fi
info "Starting PostgreSQL container..."
if command_exists podman; then
if podman ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
# Container exists but is not running
info "Starting existing PostgreSQL container..."
podman start "$CONTAINER_NAME" >/dev/null
else
# Container doesn't exist, use docker-compose
if command_exists podman-compose; then
podman-compose -f docker-compose.dev.yml up -d
else
error "podman-compose not found. Please install it or use 'make dev-db'"
return 1
fi
fi
elif command_exists docker; then
if docker ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
info "Starting existing PostgreSQL container..."
docker start "$CONTAINER_NAME" >/dev/null
else
if command_exists docker-compose; then
docker-compose -f docker-compose.dev.yml up -d
else
error "docker-compose not found. Please install it or use 'make dev-db'"
return 1
fi
fi
else
error "Neither podman nor docker found. Please install one of them."
return 1
fi
# Wait for PostgreSQL to be ready
info "Waiting for PostgreSQL to be ready..."
for i in {1..30}; do
if is_postgres_ready; then
success "PostgreSQL is ready!"
return 0
fi
sleep 1
done
error "PostgreSQL failed to become ready in time"
return 1
}
# Setup backend environment
setup_backend_env() {
if [ -f "$BACKEND_DIR/.env" ]; then
success "Backend .env file already exists"
else
if [ -f "$BACKEND_DIR/.env.example" ]; then
info "Creating backend/.env from .env.example..."
cp "$BACKEND_DIR/.env.example" "$BACKEND_DIR/.env"
success "Backend .env file created"
else
warning "Backend .env.example not found, creating default .env..."
cat > "$BACKEND_DIR/.env" <<EOF
DATABASE_URL=$DB_URL
PORT=8080
LOCATION_LAT=32.8893
LOCATION_LON=-117.2519
LOCATION_NAME=Torrey Pines Gliderport
TIMEZONE=America/Los_Angeles
FETCH_INTERVAL=15m
CACHE_TTL=10m
EOF
success "Backend .env file created with defaults"
fi
fi
}
# Setup frontend environment
setup_frontend_env() {
if [ -f "$FRONTEND_DIR/.env.local" ]; then
success "Frontend .env.local file already exists"
else
info "Creating frontend/.env.local..."
cat > "$FRONTEND_DIR/.env.local" <<EOF
NEXT_PUBLIC_API_URL=http://localhost:8080/api/v1
EOF
success "Frontend .env.local file created"
fi
}
# Run database migrations
run_migrations() {
info "Checking database migrations..."
if [ ! -d "$BACKEND_DIR/migrations" ]; then
warning "No migrations directory found, skipping..."
return 0
fi
# Check if migrations have been run by trying to query a table
# This is a simple check - you might want to use a more robust migration tool
cd "$BACKEND_DIR"
# Try to run migrations
if command_exists go; then
info "Running database migrations..."
if DATABASE_URL="$DB_URL" go run -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest -path ./migrations -database "$DB_URL" up 2>&1 | grep -q "no change"; then
success "Migrations are up to date"
else
success "Migrations applied"
fi
else
warning "Go not found, skipping migrations. Install Go to run migrations."
fi
cd - >/dev/null
}
# Check dependencies
check_dependencies() {
info "Checking dependencies..."
local missing_deps=()
if ! command_exists go; then
missing_deps+=("go (for backend)")
fi
if ! command_exists node && ! command_exists bun; then
missing_deps+=("node or bun (for frontend)")
fi
if ! command_exists podman && ! command_exists docker; then
missing_deps+=("podman or docker (for PostgreSQL)")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
warning "Missing dependencies:"
for dep in "${missing_deps[@]}"; do
echo " - $dep"
done
echo ""
else
success "All dependencies found"
fi
}
# Start backend service in background
start_backend() {
if [ -f "$BACKEND_PID_FILE" ] && kill -0 "$(cat $BACKEND_PID_FILE)" 2>/dev/null; then
warning "Backend is already running (PID: $(cat $BACKEND_PID_FILE))"
return 0
fi
if ! command_exists go; then
error "Go is required to run the backend. Please install Go first."
return 1
fi
if [ ! -f "$BACKEND_DIR/.env" ]; then
error "Backend .env file not found. Run './dev.sh' first to set up."
return 1
fi
info "Starting backend server..."
# Start backend in subshell to isolate environment
(
cd "$BACKEND_DIR"
# Export environment variables from .env file
set -a
source .env
set +a
exec go run ./cmd/api
) > "$BACKEND_LOG_FILE" 2>&1 &
echo $! > "$BACKEND_PID_FILE"
success "Backend started (PID: $(cat $BACKEND_PID_FILE), logs: $BACKEND_LOG_FILE)"
}
# Start frontend service in background
start_frontend() {
if [ -f "$FRONTEND_PID_FILE" ] && kill -0 "$(cat $FRONTEND_PID_FILE)" 2>/dev/null; then
warning "Frontend is already running (PID: $(cat $FRONTEND_PID_FILE))"
return 0
fi
local runner="npm"
if command_exists bun; then
runner="bun"
elif ! command_exists npm; then
error "Neither npm nor bun found. Please install Node.js or Bun."
return 1
fi
info "Starting frontend server with $runner..."
# Start frontend in subshell to isolate environment
(
cd "$FRONTEND_DIR"
exec $runner run dev
) > "$FRONTEND_LOG_FILE" 2>&1 &
echo $! > "$FRONTEND_PID_FILE"
success "Frontend started (PID: $(cat $FRONTEND_PID_FILE), logs: $FRONTEND_LOG_FILE)"
}
# Stop running services
stop_services() {
echo ""
info "Stopping services..."
local stopped=0
if [ -f "$BACKEND_PID_FILE" ]; then
local pid=$(cat "$BACKEND_PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
success "Backend stopped (was PID: $pid)"
stopped=1
fi
rm -f "$BACKEND_PID_FILE"
fi
if [ -f "$FRONTEND_PID_FILE" ]; then
local pid=$(cat "$FRONTEND_PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
success "Frontend stopped (was PID: $pid)"
stopped=1
fi
rm -f "$FRONTEND_PID_FILE"
fi
if [ $stopped -eq 0 ]; then
info "No services were running"
fi
echo ""
}
# Show service status
show_status() {
echo ""
info "Service Status:"
echo ""
# PostgreSQL
if is_postgres_running; then
echo " PostgreSQL: ${GREEN}${NC} Running"
else
echo " PostgreSQL: ${RED}${NC} Stopped"
fi
# Backend
if [ -f "$BACKEND_PID_FILE" ] && kill -0 "$(cat $BACKEND_PID_FILE)" 2>/dev/null; then
echo " Backend: ${GREEN}${NC} Running (PID: $(cat $BACKEND_PID_FILE))"
else
echo " Backend: ${RED}${NC} Stopped"
[ -f "$BACKEND_PID_FILE" ] && rm -f "$BACKEND_PID_FILE"
fi
# Frontend
if [ -f "$FRONTEND_PID_FILE" ] && kill -0 "$(cat $FRONTEND_PID_FILE)" 2>/dev/null; then
echo " Frontend: ${GREEN}${NC} Running (PID: $(cat $FRONTEND_PID_FILE))"
else
echo " Frontend: ${RED}${NC} Stopped"
[ -f "$FRONTEND_PID_FILE" ] && rm -f "$FRONTEND_PID_FILE"
fi
echo ""
}
# Main setup flow
main() {
echo ""
echo "🚀 Paragliding Local Development Setup"
echo "======================================"
echo ""
# Check dependencies
check_dependencies
echo ""
# Step 1: Start PostgreSQL
info "Step 1: PostgreSQL"
start_postgres || exit 1
echo ""
# Step 2: Setup environment files
info "Step 2: Environment Configuration"
setup_backend_env
setup_frontend_env
echo ""
# Step 3: Run migrations
info "Step 3: Database Migrations"
run_migrations
echo ""
# Final instructions
success "Setup complete! 🎉"
echo ""
}
# Main entry point
case "${1:-}" in
--start)
main
info "Step 4: Starting Services"
echo ""
start_backend
sleep 2 # Give backend a moment to start
start_frontend
echo ""
success "All services started! 🚀"
echo ""
echo "Services are running in the background:"
echo " - Frontend: ${BLUE}http://localhost:3000${NC}"
echo " - Backend: ${BLUE}http://localhost:8080${NC}"
echo " - API: ${BLUE}http://localhost:8080/api/v1${NC}"
echo ""
echo "View logs:"
echo " ${YELLOW}tail -f $BACKEND_LOG_FILE${NC}"
echo " ${YELLOW}tail -f $FRONTEND_LOG_FILE${NC}"
echo ""
echo "Stop services:"
echo " ${YELLOW}./dev.sh --stop${NC}"
echo ""
;;
--stop)
stop_services
;;
--status)
show_status
;;
--help)
echo "Usage: ./dev.sh [OPTION]"
echo ""
echo "Options:"
echo " (none) Setup only (PostgreSQL + .env files + migrations)"
echo " --start Setup and start backend + frontend services"
echo " --stop Stop running backend + frontend services"
echo " --status Show status of all services"
echo " --help Show this help message"
echo ""
;;
"")
main
echo "Next steps:"
echo ""
echo " Option 1 - Start services in background:"
echo " ${GREEN}./dev.sh --start${NC}"
echo ""
echo " Option 2 - Run in separate terminals:"
echo " Terminal 1: ${GREEN}make run-backend${NC}"
echo " Terminal 2: ${GREEN}make run-frontend${NC}"
echo ""
echo "Services will be available at:"
echo " - Frontend: ${BLUE}http://localhost:3000${NC}"
echo " - Backend: ${BLUE}http://localhost:8080${NC}"
echo " - API: ${BLUE}http://localhost:8080/api/v1${NC}"
echo ""
;;
*)
error "Unknown option: $1"
echo "Run './dev.sh --help' for usage information"
exit 1
;;
esac