#!/bin/sh # Nexus Docker entrypoint — wait for deps, sync .env with persistent volume, exec uvicorn. set -eu PERSIST_DIR="${NEXUS_PERSIST_DIR:-/var/lib/nexus}" PERSIST_ENV="${PERSIST_DIR}/.env" wait_tcp() { host="$1" port="$2" echo "entrypoint: waiting for ${host}:${port}..." until python3 -c " import socket s = socket.socket() s.settimeout(2) s.connect(('${host}', ${port})) s.close() " 2>/dev/null; do sleep 2 done } wait_tcp_optional() { host="$1" port="$2" tries="${3:-15}" label="${4:-service}" echo "entrypoint: waiting for ${label} ${host}:${port} (max ${tries} tries)..." n=0 while [ "$n" -lt "$tries" ]; do if python3 -c " import socket s = socket.socket() s.settimeout(2) s.connect(('${host}', ${port})) s.close() " 2>/dev/null; then echo "entrypoint: ${label} ready at ${host}:${port}" return 0 fi n=$((n + 1)) sleep 2 done echo "entrypoint: warn — ${label} not reachable at ${host}:${port}; install on host or set URL in .env" return 0 } restore_persisted_install_lock() { if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" = "1" ]; then return 0 fi PERSIST_LOCK="${PERSIST_DIR}/.install_locked" if [ -f "${PERSIST_LOCK}" ] && [ ! -f /app/.install_locked ]; then cp "${PERSIST_LOCK}" /app/.install_locked chmod 600 /app/.install_locked echo "entrypoint: restored .install_locked from ${PERSIST_LOCK}" fi } archive_install_wizard_if_locked() { if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" = "1" ]; then rm -f /app/.install_locked "${PERSIST_DIR}/.install_locked" 2>/dev/null || true if [ -f /app/web/app/install.html.bak ] && [ ! -f /app/web/app/install.html ]; then mv /app/web/app/install.html.bak /app/web/app/install.html echo "entrypoint: restored install.html from .bak (wizard pending)" fi return 0 fi if [ ! -f /app/.install_locked ] && [ ! -f "${PERSIST_DIR}/.install_locked" ]; then return 0 fi if [ -f /app/web/app/install.html.bak ]; then if [ -f /app/web/app/install.html ]; then rm -f /app/web/app/install.html echo "entrypoint: removed install.html (wizard archived as install.html.bak)" fi return 0 fi if [ -f /app/web/app/install.html ]; then mv /app/web/app/install.html /app/web/app/install.html.bak echo "entrypoint: archived install.html → install.html.bak" fi } restore_persisted_env() { if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" = "1" ]; then return 0 fi if [ ! -f "${PERSIST_ENV}" ]; then return 0 fi if ! grep -q '^NEXUS_DATABASE_URL=' "${PERSIST_ENV}" 2>/dev/null; then echo "entrypoint: warn — ${PERSIST_ENV} missing NEXUS_DATABASE_URL; skip restore (complete install wizard)" return 0 fi cp "${PERSIST_ENV}" /app/.env chmod 600 /app/.env echo "entrypoint: restored .env from ${PERSIST_ENV}" } # Compose may inject placeholder NEXUS_REDIS_URL (no password). Wizard .env wins. export_app_env() { if [ ! -f /app/.env ]; then return 0 fi set -a # shellcheck disable=SC1091 . /app/.env set +a echo "entrypoint: exported NEXUS_* from /app/.env (overrides Compose placeholders)" } write_env_from_environment() { if [ -f /app/.env ]; then return 0 fi if [ "${NEXUS_DOCKER_WRITE_ENV:-}" != "1" ]; then return 0 fi if [ -z "${NEXUS_SECRET_KEY:-}" ]; then return 0 fi if [ "${NEXUS_INSTALL_WIZARD_PENDING:-}" = "1" ]; then echo "entrypoint: install wizard pending — skip writing /app/.env" return 0 fi if [ -z "${NEXUS_DATABASE_URL:-}" ]; then echo "entrypoint: skip writing /app/.env — NEXUS_DATABASE_URL not set (use /app/install.html)" return 0 fi mkdir -p "${PERSIST_DIR}" { echo "# Generated by docker/entrypoint.sh — do not commit" env | grep '^NEXUS_' | sort } > /app/.env chmod 600 /app/.env cp /app/.env "${PERSIST_ENV}" echo "entrypoint: wrote .env from container environment" } persist_state_on_exit() { if [ -f /app/.env ]; then mkdir -p "${PERSIST_DIR}" cp /app/.env "${PERSIST_ENV}" chmod 600 "${PERSIST_ENV}" 2>/dev/null || true fi if [ -f /app/.install_locked ]; then mkdir -p "${PERSIST_DIR}" cp /app/.install_locked "${PERSIST_DIR}/.install_locked" chmod 600 "${PERSIST_DIR}/.install_locked" 2>/dev/null || true fi } trap persist_state_on_exit EXIT INT TERM # MySQL / Redis 均由用户自行安装在宿主机;安装向导阶段不阻塞启动 if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" != "1" ] && [ "${NEXUS_SKIP_MYSQL_WAIT:-0}" != "1" ]; then MYSQL_HOST="${NEXUS_1PANEL_DB_HOST:-${MYSQL_HOST:-host.docker.internal}}" MYSQL_PORT="${MYSQL_PORT:-3306}" wait_tcp_optional "${MYSQL_HOST}" "${MYSQL_PORT}" 30 "MySQL" fi if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" != "1" ] && [ "${NEXUS_SKIP_REDIS_WAIT:-0}" != "1" ]; then REDIS_HOST="${NEXUS_1PANEL_REDIS_HOST:-${REDIS_HOST:-host.docker.internal}}" REDIS_PORT="${REDIS_PORT:-6379}" wait_tcp_optional "${REDIS_HOST}" "${REDIS_PORT}" 15 "Redis" fi restore_persisted_install_lock archive_install_wizard_if_locked restore_persisted_env write_env_from_environment export_app_env exec "$@"