Files
wifi-densepose/vendor/ruvector/crates/ruvector-postgres/docker/dev.sh

386 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
# RuVector-Postgres Development Environment
# Starts PostgreSQL with hot-reload support for extension development
set -e # Exit on error
set -u # Exit on undefined variable
set -o pipefail # Exit on pipe failure
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
CONTAINER_NAME="ruvector-postgres-dev"
IMAGE_NAME="ruvector-postgres:dev"
POSTGRES_PORT="${POSTGRES_PORT:-5432}"
POSTGRES_USER="${POSTGRES_USER:-postgres}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-postgres}"
POSTGRES_DB="${POSTGRES_DB:-ruvector_dev}"
# Detect OS
OS_TYPE="$(uname -s)"
case "${OS_TYPE}" in
Linux*) PLATFORM="linux";;
Darwin*) PLATFORM="macos";;
*) PLATFORM="unknown";;
esac
# Functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[⚠]${NC} $1"
}
log_error() {
echo -e "${RED}[✗]${NC} $1"
}
log_cmd() {
echo -e "${CYAN}[$]${NC} $1"
}
check_dependencies() {
log_info "Checking dependencies..."
# Check Docker
if ! command -v docker &> /dev/null; then
log_error "Docker is not installed. Please install Docker first."
exit 1
fi
log_success "Docker found"
# Check cargo-pgrx
if ! command -v cargo-pgrx &> /dev/null; then
log_warn "cargo-pgrx not found. Installing..."
cargo install cargo-pgrx --version 0.12.6 --locked
fi
log_success "cargo-pgrx found"
}
cleanup() {
log_info "Stopping development environment..."
docker stop "${CONTAINER_NAME}" 2>/dev/null || true
docker rm "${CONTAINER_NAME}" 2>/dev/null || true
}
wait_for_postgres() {
log_info "Waiting for PostgreSQL to be ready..."
local max_attempts=30
local attempt=1
while [ ${attempt} -le ${max_attempts} ]; do
if docker exec "${CONTAINER_NAME}" pg_isready -U "${POSTGRES_USER}" &>/dev/null; then
log_success "PostgreSQL is ready!"
return 0
fi
echo -n "."
sleep 1
attempt=$((attempt + 1))
done
log_error "PostgreSQL failed to become ready"
docker logs "${CONTAINER_NAME}"
return 1
}
build_extension() {
log_info "Building ruvector-postgres extension..."
cd "${PROJECT_ROOT}/crates/ruvector-postgres"
# Build with pgrx
cargo pgrx install --pg-config "$(which pg_config)" --release
log_success "Extension built and installed"
}
start_dev_container() {
log_info "Starting development PostgreSQL container..."
# Create volume for data persistence
docker volume create "${CONTAINER_NAME}_data" || true
# Start PostgreSQL container
docker run -d \
--name "${CONTAINER_NAME}" \
-p "${POSTGRES_PORT}:5432" \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e POSTGRES_DB="${POSTGRES_DB}" \
-v "${CONTAINER_NAME}_data:/var/lib/postgresql/data" \
-v "${HOME}/.pgrx:/home/postgres/.pgrx:ro" \
--health-cmd="pg_isready -U ${POSTGRES_USER}" \
--health-interval=5s \
--health-timeout=5s \
--health-retries=5 \
postgres:16-bookworm
log_success "Container started: ${CONTAINER_NAME}"
}
setup_extension() {
log_info "Setting up extension in database..."
# Create extension
docker exec -it "${CONTAINER_NAME}" psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "CREATE EXTENSION IF NOT EXISTS ruvector_postgres CASCADE;" || {
log_warn "Extension not yet installed. Run 'cargo pgrx install' first."
return 1
}
log_success "Extension loaded successfully"
}
show_connection_info() {
local connection_string="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}"
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} RuVector-Postgres Development Environment Ready!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${CYAN}Connection String:${NC}"
echo -e " ${connection_string}"
echo ""
echo -e "${CYAN}Quick Connect Commands:${NC}"
log_cmd "psql ${connection_string}"
log_cmd "docker exec -it ${CONTAINER_NAME} psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}"
echo ""
echo -e "${CYAN}Development Workflow:${NC}"
echo -e " 1. Make changes to extension code"
echo -e " 2. Rebuild: ${YELLOW}cargo pgrx install${NC}"
echo -e " 3. Reload: ${YELLOW}docker exec ${CONTAINER_NAME} psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c 'DROP EXTENSION ruvector_postgres CASCADE; CREATE EXTENSION ruvector_postgres;'${NC}"
echo ""
echo -e "${CYAN}Useful Commands:${NC}"
log_cmd "cargo pgrx test pg16 # Run tests"
log_cmd "cargo pgrx package # Create distributable package"
log_cmd "docker logs -f ${CONTAINER_NAME} # View PostgreSQL logs"
log_cmd "docker stop ${CONTAINER_NAME} # Stop development environment"
echo ""
echo -e "${CYAN}Container Info:${NC}"
echo -e " Name: ${CONTAINER_NAME}"
echo -e " Port: ${POSTGRES_PORT}"
echo -e " User: ${POSTGRES_USER}"
echo -e " Database: ${POSTGRES_DB}"
echo -e " Platform: ${PLATFORM}"
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
}
watch_and_reload() {
log_info "Starting file watcher for hot-reload..."
log_warn "File watching requires 'cargo-watch'. Install with: cargo install cargo-watch"
cd "${PROJECT_ROOT}/crates/ruvector-postgres"
cargo watch -x "pgrx install" -s "docker exec ${CONTAINER_NAME} psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c 'DROP EXTENSION IF EXISTS ruvector_postgres CASCADE; CREATE EXTENSION ruvector_postgres;'"
}
show_usage() {
cat << EOF
RuVector-Postgres Development Environment
Usage: $0 [OPTIONS] [COMMAND]
Commands:
start Start development environment (default)
stop Stop development environment
restart Restart development environment
logs Show PostgreSQL logs
psql Open psql shell
watch Start file watcher for hot-reload
rebuild Rebuild and reload extension
status Show container status
Options:
-p, --port PORT PostgreSQL port (default: 5432)
-u, --user USER PostgreSQL user (default: postgres)
-d, --database DB PostgreSQL database (default: ruvector_dev)
-b, --background Start in background (default)
-f, --foreground Start in foreground with logs
-h, --help Show this help message
Environment Variables:
POSTGRES_PORT PostgreSQL port (default: 5432)
POSTGRES_USER PostgreSQL user (default: postgres)
POSTGRES_PASSWORD PostgreSQL password (default: postgres)
POSTGRES_DB PostgreSQL database (default: ruvector_dev)
Examples:
# Start development environment
$0 start
# Start with custom port
$0 --port 5433 start
# Open psql shell
$0 psql
# Watch for changes and auto-reload
$0 watch
# View logs
$0 logs
EOF
}
cmd_start() {
check_dependencies
# Stop existing container if running
docker stop "${CONTAINER_NAME}" 2>/dev/null || true
docker rm "${CONTAINER_NAME}" 2>/dev/null || true
start_dev_container
wait_for_postgres
# Try to setup extension if already built
setup_extension || log_warn "Run 'cargo pgrx install' to build and install the extension"
show_connection_info
}
cmd_stop() {
cleanup
log_success "Development environment stopped"
}
cmd_restart() {
cmd_stop
sleep 2
cmd_start
}
cmd_logs() {
docker logs -f "${CONTAINER_NAME}"
}
cmd_psql() {
docker exec -it "${CONTAINER_NAME}" psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}"
}
cmd_rebuild() {
log_info "Rebuilding extension..."
cd "${PROJECT_ROOT}/crates/ruvector-postgres"
cargo pgrx install --release
log_info "Reloading extension in database..."
docker exec "${CONTAINER_NAME}" psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" << 'EOF'
DROP EXTENSION IF EXISTS ruvector_postgres CASCADE;
CREATE EXTENSION ruvector_postgres;
SELECT extname, extversion FROM pg_extension WHERE extname = 'ruvector_postgres';
EOF
log_success "Extension rebuilt and reloaded!"
}
cmd_status() {
if docker ps --filter "name=${CONTAINER_NAME}" --format "{{.Names}}" | grep -q "${CONTAINER_NAME}"; then
log_success "Container ${CONTAINER_NAME} is running"
docker ps --filter "name=${CONTAINER_NAME}"
echo ""
show_connection_info
else
log_warn "Container ${CONTAINER_NAME} is not running"
echo "Start with: $0 start"
fi
}
main() {
local command="start"
local foreground=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
start|stop|restart|logs|psql|watch|rebuild|status)
command="$1"
shift
;;
-p|--port)
POSTGRES_PORT="$2"
shift 2
;;
-u|--user)
POSTGRES_USER="$2"
shift 2
;;
-d|--database)
POSTGRES_DB="$2"
shift 2
;;
-b|--background)
foreground=false
shift
;;
-f|--foreground)
foreground=true
shift
;;
-h|--help)
show_usage
exit 0
;;
*)
log_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Execute command
case "${command}" in
start)
cmd_start
if [ "${foreground}" == "true" ]; then
cmd_logs
fi
;;
stop)
cmd_stop
;;
restart)
cmd_restart
;;
logs)
cmd_logs
;;
psql)
cmd_psql
;;
watch)
watch_and_reload
;;
rebuild)
cmd_rebuild
;;
status)
cmd_status
;;
*)
log_error "Unknown command: ${command}"
show_usage
exit 1
;;
esac
}
# Run main function
main "$@"