#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" ENV_FILE="${1:-$ROOT_DIR/.env.prod}" DATA_DIR="$ROOT_DIR/data" COMPOSE_FILE="$ROOT_DIR/docker-compose.prod.yml" AGENT_TEMPLATES_FILE="$DATA_DIR/templates/agent_md_templates.json" TOPIC_PRESETS_FILE="$DATA_DIR/templates/topic_presets.json" SKILLS_DIR="$DATA_DIR/skills" require_file() { local path="$1" local hint="$2" if [[ -f "$path" ]]; then return 0 fi echo "Missing file: $path" if [[ -n "$hint" ]]; then echo "$hint" fi exit 1 } require_dir() { local path="$1" local hint="$2" if [[ -d "$path" ]]; then return 0 fi echo "Missing directory: $path" if [[ -n "$hint" ]]; then echo "$hint" fi exit 1 } require_env() { local name="$1" if [[ -n "${!name:-}" ]]; then return 0 fi echo "Missing required env: $name" exit 1 } read_env_value() { local key="$1" local line="" local value="" while IFS= read -r line || [[ -n "$line" ]]; do line="${line%$'\r'}" [[ -z "${line//[[:space:]]/}" ]] && continue [[ "${line#\#}" != "$line" ]] && continue [[ "${line#export }" != "$line" ]] && line="${line#export }" [[ "$line" == "$key="* ]] || continue value="${line#*=}" if [[ "$value" =~ ^\"(.*)\"$ ]]; then value="${BASH_REMATCH[1]}" elif [[ "$value" =~ ^\'(.*)\'$ ]]; then value="${BASH_REMATCH[1]}" fi printf '%s' "$value" return 0 done < "$ENV_FILE" return 1 } load_env_var() { local name="$1" local default_value="${2:-}" local value="" value="$(read_env_value "$name" || true)" if [[ -z "$value" ]]; then value="$default_value" fi printf -v "$name" '%s' "$value" } if [[ ! -f "$ENV_FILE" ]]; then echo "[WARNING] Missing env file: $ENV_FILE" echo "[WARNING] Creating it from: $ROOT_DIR/.env.prod.example ..." cp "$ROOT_DIR/.env.prod.example" "$ENV_FILE" echo "[WARNING] 请编辑 $ENV_FILE 文件,配置正确的数据库、Redis、端口等参数" echo "[WARNING] 按任意键继续,或 Ctrl+C 退出..." read -n 1 -s fi require_file "$COMPOSE_FILE" "" require_file "$AGENT_TEMPLATES_FILE" "Expected tracked template file under project-root data/templates/" require_file "$TOPIC_PRESETS_FILE" "Expected tracked template file under project-root data/templates/" require_dir "$SKILLS_DIR" "Expected tracked skills directory under project-root data/skills/" load_env_var HOST_BOTS_WORKSPACE_ROOT load_env_var DATABASE_URL load_env_var NGINX_PORT 8080 load_env_var REDIS_ENABLED false load_env_var REDIS_URL require_env HOST_BOTS_WORKSPACE_ROOT require_env DATABASE_URL require_env NGINX_PORT if [[ "$DATABASE_URL" != postgresql* ]]; then echo "Unsupported DATABASE_URL for deploy-prod.sh: $DATABASE_URL" echo "deploy-prod.sh now supports external PostgreSQL only." echo "If you need one-click PostgreSQL + Redis deployment, use scripts/deploy-full.sh." exit 1 fi if [[ "${REDIS_ENABLED,,}" =~ ^(1|true|yes|on)$ ]] && [[ -z "$REDIS_URL" ]]; then echo "Missing required env: REDIS_URL" exit 1 fi echo "[deploy] using env: $ENV_FILE" mkdir -p "$DATA_DIR" "$DATA_DIR/model" "$HOST_BOTS_WORKSPACE_ROOT" echo "[deploy] expecting external PostgreSQL to be pre-initialized with scripts/sql/create-tables.sql and scripts/sql/init-data.sql" docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" config -q docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d --build echo "[deploy] service status" docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" ps echo "[deploy] done"