init
This commit is contained in:
447
dev.sh
Executable file
447
dev.sh
Executable file
@@ -0,0 +1,447 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user