| # ============================================================================= | |
| # run_docker.sh — IE Research Platform · Docker Launcher | |
| # ============================================================================= | |
| # Usage: | |
| # ./run_docker.sh # build + start all services (default: up) | |
| # ./run_docker.sh build # only build images (no-cache) | |
| # ./run_docker.sh up # start without rebuilding | |
| # ./run_docker.sh down # stop and remove containers | |
| # ./run_docker.sh restart # down then up | |
| # ./run_docker.sh logs # tail logs of all services | |
| # ./run_docker.sh logs backend # tail logs of one service | |
| # ./run_docker.sh status # show running containers | |
| # ./run_docker.sh clean # remove containers, images & volumes (DATA LOSS) | |
| # ./run_docker.sh help # show this help | |
| # ============================================================================= | |
| set -euo pipefail | |
| # ── Colour helpers ───────────────────────────────────────────────────────── | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' | |
| info() { echo -e "${CYAN}${BOLD}ℹ $*${RESET}"; } | |
| success() { echo -e "${GREEN}${BOLD}✔ $*${RESET}"; } | |
| warn() { echo -e "${YELLOW}${BOLD}⚠ $*${RESET}"; } | |
| error() { echo -e "${RED}${BOLD}✘ $*${RESET}" >&2; } | |
| step() { echo -e "\n${BLUE}${BOLD}══ $* ${RESET}"; } | |
| banner() { | |
| echo -e "${CYAN}${BOLD}" | |
| echo " ██████╗ ███████╗███████╗███████╗ █████╗ ██████╗ ██████╗██╗ ██╗" | |
| echo " ██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██║" | |
| echo " ██████╔╝█████╗ ███████╗█████╗ ███████║██████╔╝██║ ███████║" | |
| echo " ██╔══██╗██╔══╝ ╚════██║██╔══╝ ██╔══██║██╔══██╗██║ ██╔══██║" | |
| echo " ██║ ██║███████╗███████║███████╗██║ ██║██║ ██║╚██████╗██║ ██║" | |
| echo " ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝" | |
| echo -e " IE Research Platform — Docker Launcher${RESET}" | |
| echo "" | |
| } | |
| # ── Paths ────────────────────────────────────────────────────────────────── | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | |
| cd "$SCRIPT_DIR" | |
| COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml" | |
| ENV_FILE="$SCRIPT_DIR/.env" | |
| ENV_EXAMPLE="$SCRIPT_DIR/.env.example" | |
| banner | |
| # ============================================================================= | |
| # STEP 1 — Find a working Docker daemon | |
| # ============================================================================= | |
| step "Checking Docker daemon" | |
| COLIMA_SOCKETS=("$HOME/.colima/default/docker.sock" "$HOME/.colima/docker.sock") | |
| # ── Set Colima DNS (Iranian anti-sanction DNS - Shecan) ──────────────── | |
| set_colima_dns() { | |
| local colima_yaml="$HOME/.colima/default/colima.yaml" | |
| if [[ ! -f "$colima_yaml" ]]; then | |
| warn "Colima config not found at $colima_yaml — skipping DNS setup." | |
| return | |
| fi | |
| # Check if DNS is already set | |
| if grep -q '^dns:' "$colima_yaml" 2>/dev/null; then | |
| # Replace the dns line with Shecan DNS | |
| sed -i.bak 's/^dns:.*/dns:\n - 178.22.122.100\n - 185.51.200.2/' "$colima_yaml" | |
| else | |
| # Insert dns section under "network:" | |
| sed -i.bak '/^network:/a\ | |
| dns:\n - 178.22.122.100\n - 185.51.200.2' "$colima_yaml" | |
| fi | |
| success "Colima DNS set to Shecan (178.22.122.100, 185.51.200.2)" | |
| } | |
| find_docker() { | |
| # 1. Already reachable with current DOCKER_HOST / context? | |
| if docker info >/dev/null 2>&1; then | |
| success "Docker daemon is reachable." | |
| return 0 | |
| fi | |
| # 2. Try every known Colima socket directly (most reliable) | |
| local all_sockets=( | |
| "$HOME/.colima/default/docker.sock" | |
| "$HOME/.colima/docker.sock" | |
| "/var/run/docker.sock" | |
| ) | |
| for sock in "${all_sockets[@]}"; do | |
| if [[ -S "$sock" ]]; then | |
| export DOCKER_HOST="unix://$sock" | |
| if docker info >/dev/null 2>&1; then | |
| success "Docker daemon reachable via $sock" | |
| return 0 | |
| fi | |
| fi | |
| done | |
| # 3. Also try any docker context that has a valid endpoint | |
| while IFS= read -r ctx_sock; do | |
| [[ -z "$ctx_sock" ]] && continue | |
| export DOCKER_HOST="unix://$ctx_sock" | |
| if docker info >/dev/null 2>&1; then | |
| success "Docker daemon reachable via context socket: $ctx_sock" | |
| return 0 | |
| fi | |
| done < <(docker context ls --format '{{.DockerEndpoint}}' 2>/dev/null | sed -n 's/^unix:\/\///p' || true) | |
| # 4. Try to start Colima if installed | |
| if command -v colima >/dev/null 2>&1; then | |
| # Check if Colima is actually running (status 'Running') | |
| if colima list 2>/dev/null | grep -q 'Running'; then | |
| info "Colima is running — trying sockets..." | |
| sleep 5 | |
| for sock in "${all_sockets[@]}"; do | |
| if [[ -S "$sock" ]]; then | |
| export DOCKER_HOST="unix://$sock" | |
| if docker info >/dev/null 2>&1; then | |
| success "Docker daemon reachable via $sock" | |
| return 0 | |
| fi | |
| fi | |
| done | |
| else | |
| info "Starting Colima (2 CPU, 4 GB RAM, 60 GB disk)..." | |
| colima start --cpu 2 --memory 4 --disk 60 | |
| sleep 8 | |
| for sock in "${all_sockets[@]}"; do | |
| if [[ -S "$sock" ]]; then | |
| export DOCKER_HOST="unix://$sock" | |
| if docker info >/dev/null 2>&1; then | |
| success "Colima started. Using $sock" | |
| return 0 | |
| fi | |
| fi | |
| done | |
| fi | |
| fi | |
| error "No reachable Docker daemon found." | |
| echo "" | |
| echo " Tried sockets:" | |
| for sock in "${all_sockets[@]}"; do | |
| echo " $sock → $([ -S "$sock" ] && echo 'socket exists but Docker not responding' || echo 'not found')" | |
| done | |
| echo "" | |
| echo " Fix options:" | |
| echo " colima start # if Colima is installed" | |
| echo " brew install colima && colima start" | |
| exit 1 | |
| } | |
| find_docker | |
| # ============================================================================= | |
| # STEP 2 — Configure registry mirrors (for Iran / blocked networks) | |
| # ============================================================================= | |
| step "Configuring Docker registry mirror" | |
| # Detect runtime type for correct daemon.json path | |
| DOCKER_RUNTIME="unknown" | |
| if [[ -S "$HOME/.colima/default/docker.sock" ]] || [[ -S "$HOME/.colima/docker.sock" ]]; then | |
| DOCKER_RUNTIME="colima" | |
| elif [[ "$DOCKER_HOST" == *"colima"* ]]; then | |
| DOCKER_RUNTIME="colima" | |
| elif [[ "$OSTYPE" == "linux"* ]]; then | |
| DOCKER_RUNTIME="linux" | |
| elif [[ "$OSTYPE" == "darwin"* ]]; then | |
| # Check if Docker Desktop app is running | |
| if pgrep -x "Docker" >/dev/null 2>&1; then | |
| DOCKER_RUNTIME="docker-desktop" | |
| else | |
| DOCKER_RUNTIME="docker-desktop" | |
| fi | |
| fi | |
| info "Runtime: $DOCKER_RUNTIME" | |
| # The mirror JSON block | |
| MIRRORS_JSON='{ | |
| "registry-mirrors": [ | |
| "https://docker.arvancloud.ir", | |
| "https://registry.docker.ir", | |
| "https://dockerhub.ir" | |
| ] | |
| }' | |
| # Write mirrors into a daemon.json, merging with existing content | |
| write_daemon_json() { | |
| local daemon_json="$1" | |
| local dir | |
| dir="$(dirname "$daemon_json")" | |
| mkdir -p "$dir" 2>/dev/null || true | |
| # Already has mirrors? Return 1 = no change needed, skip restart | |
| if [[ -f "$daemon_json" ]] && grep -q "arvancloud\|docker.ir" "$daemon_json" 2>/dev/null; then | |
| success "Mirrors already set in $daemon_json" | |
| return 1 # <-- signals "no change, no restart needed" | |
| fi | |
| warn "Writing Iranian mirrors to $daemon_json" | |
| local content="$MIRRORS_JSON" | |
| # If a daemon.json already exists, merge rather than overwrite | |
| if [[ -f "$daemon_json" ]] && [[ -s "$daemon_json" ]]; then | |
| local merged | |
| merged=$(python3 -c " | |
| import json | |
| with open('$daemon_json') as fh: | |
| cfg = json.load(fh) | |
| cfg['registry-mirrors'] = [ | |
| 'https://docker.arvancloud.ir', | |
| 'https://registry.docker.ir', | |
| 'https://dockerhub.ir' | |
| ] | |
| print(json.dumps(cfg, indent=2)) | |
| " 2>/dev/null) && content="$merged" || true | |
| fi | |
| # Write the file (try direct write first, then sudo) | |
| if [[ -w "$dir" ]]; then | |
| printf '%s\n' "$content" > "$daemon_json" | |
| elif command -v sudo >/dev/null 2>&1; then | |
| printf '%s\n' "$content" | sudo tee "$daemon_json" >/dev/null | |
| else | |
| error "Cannot write to $daemon_json — no permission and sudo unavailable." | |
| exit 1 | |
| fi | |
| success "Written: $daemon_json" | |
| return 0 # <-- signals "changed, restart needed" | |
| } | |
| # Check if Docker Hub is reachable (fast DNS probe, no image pull) | |
| dockerhub_ok=false | |
| if curl -sf --max-time 5 --head "https://registry-1.docker.io" -o /dev/null 2>&1; then | |
| dockerhub_ok=true | |
| fi | |
| if [[ "$dockerhub_ok" == "true" ]]; then | |
| success "Docker Hub reachable — no mirror needed." | |
| else | |
| warn "Docker Hub not reachable. Applying Iranian mirrors..." | |
| case "$DOCKER_RUNTIME" in | |
| colima) | |
| # Colima's daemon config lives here; it is read on every `colima start` | |
| DAEMON_JSON="$HOME/.colima/default/daemon.json" | |
| if write_daemon_json "$DAEMON_JSON"; then | |
| # daemon.json was changed — must restart Colima to pick it up | |
| set_colima_dns | |
| success "Restarting Colima to apply mirrors and DNS..." | |
| colima stop 2>/dev/null || true | |
| colima start | |
| sleep 8 | |
| # Re-export DOCKER_HOST | |
| for sock in "${COLIMA_SOCKETS[@]}"; do | |
| if [[ -S "$sock" ]]; then | |
| export DOCKER_HOST="unix://$sock" | |
| break | |
| fi | |
| done | |
| success "Colima restarted with Iranian mirrors." | |
| else | |
| # Mirrors already in place — skip restart, preserve Docker layer cache | |
| success "Mirrors already configured. Skipping Colima restart (layer cache preserved)." | |
| fi | |
| ;; | |
| linux) | |
| write_daemon_json "/etc/docker/daemon.json" | |
| if command -v sudo >/dev/null 2>&1; then | |
| sudo systemctl restart docker | |
| sleep 5 | |
| success "Docker daemon restarted." | |
| else | |
| warn "Restart Docker manually: sudo systemctl restart docker" | |
| exit 1 | |
| fi | |
| ;; | |
| docker-desktop) | |
| write_daemon_json "$HOME/.docker/daemon.json" | |
| echo "" | |
| warn "Restart Docker Desktop for the mirror to take effect:" | |
| warn " Docker Desktop → Settings → Docker Engine → Apply & Restart" | |
| echo "" | |
| printf " Press ENTER once Docker Desktop has restarted (Ctrl-C to abort)... " >&2 | |
| read -r _dummy </dev/tty | |
| ;; | |
| *) | |
| write_daemon_json "$HOME/.docker/daemon.json" | |
| warn "Unknown runtime — please restart your Docker daemon manually." | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| # ============================================================================= | |
| # STEP 3 — Environment file | |
| # ============================================================================= | |
| step "Checking environment configuration" | |
| if [[ ! -f "$ENV_FILE" ]]; then | |
| if [[ -f "$ENV_EXAMPLE" ]]; then | |
| cp "$ENV_EXAMPLE" "$ENV_FILE" | |
| warn ".env created from .env.example — please review it." | |
| echo "" | |
| echo " Edit these values before continuing:" | |
| echo " MYSQL_PASSWORD → strong password" | |
| echo " MYSQL_ROOT_PASSWORD → strong root password" | |
| echo " JWT_SECRET → openssl rand -hex 64" | |
| echo "" | |
| printf " Press ENTER once you have edited .env (Ctrl-C to abort)... " >&2 | |
| read -r _dummy </dev/tty | |
| else | |
| error ".env.example not found. Cannot create .env." | |
| exit 1 | |
| fi | |
| else | |
| success ".env file found." | |
| fi | |
| # Validate required vars | |
| required_vars=(MYSQL_DATABASE MYSQL_USER MYSQL_PASSWORD MYSQL_ROOT_PASSWORD JWT_SECRET) | |
| missing=() | |
| for var in "${required_vars[@]}"; do | |
| val=$(grep -E "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d= -f2- | tr -d '"' | xargs 2>/dev/null || true) | |
| if [[ -z "$val" || "$val" == change_me* || "$val" == your-* ]]; then | |
| missing+=("$var") | |
| fi | |
| done | |
| if [[ ${#missing[@]} -gt 0 ]]; then | |
| error "These .env variables are unset or still have placeholder values:" | |
| for v in "${missing[@]}"; do echo " → $v"; done | |
| echo "" | |
| echo " Edit $ENV_FILE then rerun." | |
| exit 1 | |
| fi | |
| success "All required environment variables are set." | |
| # ============================================================================= | |
| # STEP 4 — Pick compose binary | |
| # ============================================================================= | |
| CMD="${1:-up}" | |
| compose_cmd() { | |
| # Enable BuildKit so Dockerfile --mount=type=cache directives work | |
| export DOCKER_BUILDKIT=1 | |
| export COMPOSE_DOCKER_CLI_BUILD=1 | |
| if docker compose version >/dev/null 2>&1; then | |
| docker compose "$@" | |
| elif command -v docker-compose >/dev/null 2>&1; then | |
| docker-compose "$@" | |
| else | |
| error "Neither 'docker compose' nor 'docker-compose' found." | |
| exit 1 | |
| fi | |
| } | |
| compile_backend() { | |
| step "Compiling Backend App on host Mac (instantly uses host Maven cache)" | |
| if command -v mvn >/dev/null 2>&1; then | |
| export MAVEN_OPTS="-Xmx512m -XX:+UseSerialGC" | |
| if mvn clean package -Dmaven.test.skip=true -B; then | |
| success "Backend compiled successfully on host!" | |
| else | |
| error "Failed to compile Backend on host Mac. Please check errors above." | |
| exit 1 | |
| fi | |
| else | |
| error "Maven (mvn) is not installed on your host Mac. Please install Maven first: 'brew install maven'" | |
| exit 1 | |
| fi | |
| } | |
| compile_frontend() { | |
| step "Compiling Frontend App on host Mac (instantly uses host Yarn cache)" | |
| if command -v yarn >/dev/null 2>&1; then | |
| cd "$SCRIPT_DIR/front" | |
| info "Installing frontend dependencies on host..." | |
| yarn install --frozen-lockfile | |
| info "Building frontend assets on host..." | |
| VITE_API_BASE_URL="${VITE_API_BASE_URL:-/api}" \ | |
| VITE_WS_URL="${VITE_WS_URL:-}" \ | |
| VITE_COLLAB_WS_URL="${VITE_COLLAB_WS_URL:-}" \ | |
| VITE_REAL_API="${VITE_REAL_API:-true}" \ | |
| VITE_WS_ENABLED="${VITE_WS_ENABLED:-true}" \ | |
| yarn build | |
| cd "$SCRIPT_DIR" | |
| success "Frontend compiled successfully on host!" | |
| else | |
| error "Yarn is not installed on your host Mac. Please install Yarn first: 'brew install yarn'" | |
| exit 1 | |
| fi | |
| } | |
| # ============================================================================= | |
| # COMMANDS | |
| # ============================================================================= | |
| case "$CMD" in | |
| build) | |
| step "Building Docker images using Multi-Stage Docker Build" | |
| compose_cmd -f "$COMPOSE_FILE" build | |
| success "All images built." | |
| ;; | |
| rebuild) | |
| step "Force-rebuilding Docker images (no-cache)" | |
| compose_cmd -f "$COMPOSE_FILE" build --no-cache --pull | |
| success "All images rebuilt." | |
| ;; | |
| up) | |
| step "Building and starting all services via Docker Compose" | |
| compose_cmd -f "$COMPOSE_FILE" up --build -d | |
| echo "" | |
| success "All services started!" | |
| echo -e " ${BOLD}🌐 Frontend :${RESET} http://ie-research.local (or http://localhost:5173)" | |
| echo -e " ${BOLD}⚙ Backend API:${RESET} http://ie-research.local/api (or http://localhost:9092/api)" | |
| echo -e " ${BOLD}🔌 WebSocket :${RESET} ws://ie-research.local/ws (or ws://localhost:9093/ws)" | |
| echo -e " ${BOLD}🗄 Database :${RESET} localhost:3306" | |
| echo "" | |
| echo -e " ${YELLOW}${BOLD}⚠ NOTE: To use 'ie-research.local', add this line to your /etc/hosts file:${RESET}" | |
| echo -e " 127.0.0.1 ie-research.local" | |
| echo "" | |
| info "Stream logs : ./run_docker.sh logs" | |
| info "Stop all : ./run_docker.sh down" | |
| ;; | |
| down) | |
| step "Stopping all services" | |
| compose_cmd -f "$COMPOSE_FILE" down | |
| success "All services stopped." | |
| ;; | |
| restart) | |
| step "Restarting all services" | |
| compose_cmd -f "$COMPOSE_FILE" down | |
| compose_cmd -f "$COMPOSE_FILE" up --build -d | |
| success "All services restarted." | |
| ;; | |
| logs) | |
| SERVICE="${2:-}" | |
| if [[ -n "$SERVICE" ]]; then | |
| step "Logs: $SERVICE" | |
| compose_cmd -f "$COMPOSE_FILE" logs -f "$SERVICE" | |
| else | |
| step "Logs: all services" | |
| compose_cmd -f "$COMPOSE_FILE" logs -f | |
| fi | |
| ;; | |
| status) | |
| step "Service status" | |
| compose_cmd -f "$COMPOSE_FILE" ps | |
| ;; | |
| clean) | |
| warn "This removes ALL containers, images, and volumes for this project." | |
| warn "ALL DATABASE DATA WILL BE LOST." | |
| echo "" | |
| printf " Type 'yes' to confirm: " >&2 | |
| read -r confirm </dev/tty | |
| if [[ "$confirm" == "yes" ]]; then | |
| step "Cleaning up" | |
| compose_cmd -f "$COMPOSE_FILE" down --rmi all --volumes --remove-orphans | |
| if [[ -f "cli/Makefile" ]]; then | |
| make -C cli clean >/dev/null 2>&1 || true | |
| fi | |
| success "Clean done." | |
| else | |
| info "Cancelled." | |
| fi | |
| ;; | |
| help|--help|-h) | |
| echo "" | |
| echo -e "${BOLD}Usage:${RESET} ./run_docker.sh [command] [service]" | |
| echo "" | |
| echo -e "${BOLD}Commands:${RESET}" | |
| echo " (none) / up Build images and start all services (detached)" | |
| echo " build Rebuild all Docker images (no-cache)" | |
| echo " down Stop and remove containers" | |
| echo " restart down + up" | |
| echo " logs [svc] Stream logs (svc: backend | frontend | db)" | |
| echo " status Show container status" | |
| echo " clean Remove containers, images, volumes ⚠ DATA LOSS" | |
| echo " help Show this message" | |
| echo "" | |
| echo -e "${BOLD}Services:${RESET} db · backend · frontend" | |
| echo "" | |
| echo -e "${BOLD}Examples:${RESET}" | |
| echo " ./run_docker.sh # start everything" | |
| echo " ./run_docker.sh logs backend # stream backend logs" | |
| echo " ./run_docker.sh down # stop everything" | |
| echo "" | |
| ;; | |
| *) | |
| error "Unknown command: $CMD" | |
| echo " Run './run_docker.sh help' for usage." | |
| exit 1 | |
| ;; | |
| esac |
Xet Storage Details
- Size:
- 18.4 kB
- Xet hash:
- ab306178fc31ee136281656eeefa843089e0639a116ad2b9da07913995032251
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.