tahamajs/IE / Projects /scripts /run_docker.sh
tahamajs's picture
download
raw
18.4 kB
#!/usr/bin/env bash
# =============================================================================
# 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.