diff --git a/.env.template b/.env.template index fbb37b4..1973a78 100644 --- a/.env.template +++ b/.env.template @@ -14,7 +14,7 @@ COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=0 # Project name # ===================== # Customize this to match your deployment slug (used for container names/tags) -COMPOSE_PROJECT_NAME=azerothcore-stack +COMPOSE_PROJECT_NAME=azerothcore-realmmaster # ===================== # Storage & Timezone @@ -96,15 +96,15 @@ AC_DB_IMPORT_IMAGE=acore/ac-wotlk-db-import:master AC_AUTHSERVER_IMAGE=acore/ac-wotlk-authserver:master AC_WORLDSERVER_IMAGE=acore/ac-wotlk-worldserver:master # Services (Playerbots) -AC_AUTHSERVER_IMAGE_PLAYERBOTS=azerothcore-realmmaster:authserver-playerbots -AC_WORLDSERVER_IMAGE_PLAYERBOTS=azerothcore-realmmaster:worldserver-playerbots +AC_AUTHSERVER_IMAGE_PLAYERBOTS=${COMPOSE_PROJECT_NAME}:authserver-playerbots +AC_WORLDSERVER_IMAGE_PLAYERBOTS=${COMPOSE_PROJECT_NAME}:worldserver-playerbots # Services (Module Build Tags) # Images used during module compilation and tagging -AC_AUTHSERVER_IMAGE_MODULES=azerothcore-realmmaster:authserver-modules-latest -AC_WORLDSERVER_IMAGE_MODULES=azerothcore-realmmaster:worldserver-modules-latest +AC_AUTHSERVER_IMAGE_MODULES=${COMPOSE_PROJECT_NAME}:authserver-modules-latest +AC_WORLDSERVER_IMAGE_MODULES=${COMPOSE_PROJECT_NAME}:worldserver-modules-latest # Client Data AC_CLIENT_DATA_IMAGE=acore/ac-wotlk-client-data:master -AC_CLIENT_DATA_IMAGE_PLAYERBOTS=uprightbass360/azerothcore-wotlk-playerbots:client-data-Playerbot +AC_CLIENT_DATA_IMAGE_PLAYERBOTS=${COMPOSE_PROJECT_NAME}:client-data-playerbots # Build artifacts DOCKER_IMAGE_TAG=master AC_AUTHSERVER_IMAGE_BASE=acore/ac-wotlk-authserver diff --git a/.gitignore b/.gitignore index ff61026..8876ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ images/ node_modules/ .mcp*/ scripts/__pycache__/* +scripts/bash/__pycache__/* scripts/python/__pycache__/* .env package-lock.json diff --git a/changelog.sh b/changelog.sh index 53144e4..0322a3c 100755 --- a/changelog.sh +++ b/changelog.sh @@ -99,7 +99,36 @@ done # Get last build time from container metadata get_last_build_time() { local containers=("ac-worldserver" "ac-authserver") - local images=("azerothcore-stack:worldserver-playerbots" "azerothcore-stack:authserver-playerbots") + local images=() + + # Require COMPOSE_PROJECT_NAME to be set + if [[ -z "${COMPOSE_PROJECT_NAME:-}" ]]; then + warn "COMPOSE_PROJECT_NAME not set in environment" + return 1 + fi + + # Use actual image names from environment + # Detect variant to check appropriate images + if [[ "${STACK_IMAGE_MODE:-standard}" == "playerbots" ]] || [[ "${MODULE_PLAYERBOTS:-0}" == "1" ]] || [[ "${PLAYERBOT_ENABLED:-0}" == "1" ]] || [[ "${STACK_SOURCE_VARIANT:-}" == "playerbots" ]]; then + if [[ -z "${AC_WORLDSERVER_IMAGE_PLAYERBOTS:-}" ]] || [[ -z "${AC_AUTHSERVER_IMAGE_PLAYERBOTS:-}" ]]; then + warn "Playerbots mode detected but AC_WORLDSERVER_IMAGE_PLAYERBOTS or AC_AUTHSERVER_IMAGE_PLAYERBOTS not set" + return 1 + fi + images=( + "${AC_WORLDSERVER_IMAGE_PLAYERBOTS}" + "${AC_AUTHSERVER_IMAGE_PLAYERBOTS}" + ) + else + if [[ -z "${AC_WORLDSERVER_IMAGE:-}" ]] || [[ -z "${AC_AUTHSERVER_IMAGE:-}" ]]; then + warn "Standard mode detected but AC_WORLDSERVER_IMAGE or AC_AUTHSERVER_IMAGE not set" + return 1 + fi + images=( + "${AC_WORLDSERVER_IMAGE}" + "${AC_AUTHSERVER_IMAGE}" + ) + fi + local latest_date="" # Try to get build timestamp from containers and images @@ -143,7 +172,7 @@ if [[ -n "$SINCE_DATE" ]]; then DATE_DESC="since $SINCE_DATE" else # Try to use last build time as default - LAST_BUILD_DATE=$(get_last_build_time) + LAST_BUILD_DATE=$(get_last_build_time 2>/dev/null) || LAST_BUILD_DATE="" if [[ -n "$LAST_BUILD_DATE" ]]; then SINCE_OPTION="--since=$LAST_BUILD_DATE" @@ -194,11 +223,17 @@ detect_source_config() { $VERBOSE && log "Switched to playerbots variant" >&2 fi - # Repository URLs from environment or defaults - local standard_repo="${ACORE_REPO_STANDARD:-https://github.com/azerothcore/azerothcore-wotlk.git}" - local standard_branch="${ACORE_BRANCH_STANDARD:-master}" - local playerbots_repo="${ACORE_REPO_PLAYERBOTS:-https://github.com/mod-playerbots/azerothcore-wotlk.git}" - local playerbots_branch="${ACORE_BRANCH_PLAYERBOTS:-Playerbot}" + # Repository URLs from environment (required) + local standard_repo="${ACORE_REPO_STANDARD}" + local standard_branch="${ACORE_BRANCH_STANDARD}" + local playerbots_repo="${ACORE_REPO_PLAYERBOTS}" + local playerbots_branch="${ACORE_BRANCH_PLAYERBOTS}" + + if [[ -z "$standard_repo" ]] || [[ -z "$standard_branch" ]] || [[ -z "$playerbots_repo" ]] || [[ -z "$playerbots_branch" ]]; then + warn "Repository configuration missing from environment" + warn "Required: ACORE_REPO_STANDARD, ACORE_BRANCH_STANDARD, ACORE_REPO_PLAYERBOTS, ACORE_BRANCH_PLAYERBOTS" + return 1 + fi if [[ "$variant" == "playerbots" ]]; then echo "$playerbots_repo|$playerbots_branch|$LOCAL_STORAGE_ROOT/source/azerothcore-playerbots" diff --git a/cleanup.sh b/cleanup.sh index 1f62628..7e011da 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -146,8 +146,6 @@ sanitize_project_name(){ project_name::sanitize "$1" } -PROJECT_IMAGE_PREFIX="$(sanitize_project_name "${COMPOSE_PROJECT_NAME:-$DEFAULT_PROJECT_NAME}")" - remove_storage_dir(){ local path="$1" if [ -d "$path" ]; then @@ -223,8 +221,7 @@ nuclear_cleanup() { # Remove project images (server/tool images typical to this project) execute_command "Remove acore images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E '^acore/' | xargs -r docker rmi" - execute_command "Remove local project images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E '^${PROJECT_IMAGE_PREFIX}:' | xargs -r docker rmi" - execute_command "Remove legacy playerbots images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E '^uprightbass360/azerothcore-wotlk-playerbots' | xargs -r docker rmi" + execute_command "Remove project-specific images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E \"^${PROJECT_NAME}:\" | xargs -r docker rmi" execute_command "Remove tool images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E 'phpmyadmin|uprightbass360/keira3' | xargs -r docker rmi" # Storage cleanup (preserve backups if requested) diff --git a/docs/PHASE1_INTEGRATION_TEST_SUMMARY.md b/docs/PHASE1_INTEGRATION_TEST_SUMMARY.md index c572f7b..8b961b0 100644 --- a/docs/PHASE1_INTEGRATION_TEST_SUMMARY.md +++ b/docs/PHASE1_INTEGRATION_TEST_SUMMARY.md @@ -142,7 +142,7 @@ MODULES_ENABLED="mod-playerbots mod-aoe-loot ..." **What Gets Built:** - AzerothCore with playerbots branch - 93 modules compiled and integrated in this run (current manifest: 348 total / 221 supported) -- Custom Docker images: `acore-compose:worldserver-modules-latest` etc. +- Custom Docker images: `${COMPOSE_PROJECT_NAME}:worldserver-modules-latest` etc. ### Deployment Status: READY TO DEPLOY 🚀 diff --git a/scripts/bash/statusjson.sh b/scripts/bash/statusjson.sh index fc90020..e70af62 100755 --- a/scripts/bash/statusjson.sh +++ b/scripts/bash/statusjson.sh @@ -4,6 +4,7 @@ import os import re import socket import subprocess +import sys import time from pathlib import Path @@ -468,8 +469,14 @@ def docker_stats(): def main(): env = load_env() - project = read_env(env, "COMPOSE_PROJECT_NAME", "acore-compose") - network = read_env(env, "NETWORK_NAME", "azerothcore") + project = read_env(env, "COMPOSE_PROJECT_NAME") + if not project: + print(json.dumps({"error": "COMPOSE_PROJECT_NAME not set in environment"}), file=sys.stderr) + sys.exit(1) + network = read_env(env, "NETWORK_NAME") + if not network: + print(json.dumps({"error": "NETWORK_NAME not set in environment"}), file=sys.stderr) + sys.exit(1) services = [ ("ac-mysql", "MySQL"), diff --git a/scripts/bash/validate-env.sh b/scripts/bash/validate-env.sh new file mode 100755 index 0000000..1e531fd --- /dev/null +++ b/scripts/bash/validate-env.sh @@ -0,0 +1,301 @@ +#!/bin/bash +# Validate environment configuration for AzerothCore RealmMaster +# Usage: ./scripts/bash/validate-env.sh [--strict] [--quiet] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +ENV_FILE="$PROJECT_ROOT/.env" +TEMPLATE_FILE="$PROJECT_ROOT/.env.template" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Flags +STRICT_MODE=false +QUIET_MODE=false +EXIT_CODE=0 + +# Parse arguments +while [[ $# -gt 0 ]]; do + case "$1" in + --strict) + STRICT_MODE=true + shift + ;; + --quiet) + QUIET_MODE=true + shift + ;; + -h|--help) + cat <&2 + exit 1 + ;; + esac +done + +log_info() { + $QUIET_MODE || echo -e "${BLUE}â„šī¸ $*${NC}" +} + +log_success() { + $QUIET_MODE || echo -e "${GREEN}✅ $*${NC}" +} + +log_warning() { + echo -e "${YELLOW}âš ī¸ $*${NC}" >&2 +} + +log_error() { + echo -e "${RED}❌ $*${NC}" >&2 +} + +# Load environment +load_env() { + local file="$1" + if [[ ! -f "$file" ]]; then + return 1 + fi + + set -a + # shellcheck disable=SC1090 + source "$file" 2>/dev/null || return 1 + set +a + return 0 +} + +# Check if variable is set and non-empty +check_var() { + local var_name="$1" + local var_value="${!var_name:-}" + + if [[ -z "$var_value" ]]; then + return 1 + fi + return 0 +} + +# Validate required variables +validate_required() { + local missing=() + + local required_vars=( + # Project Configuration + "COMPOSE_PROJECT_NAME" + "NETWORK_NAME" + # Repository Configuration + "ACORE_REPO_STANDARD" + "ACORE_BRANCH_STANDARD" + "ACORE_REPO_PLAYERBOTS" + "ACORE_BRANCH_PLAYERBOTS" + # Storage Paths + "STORAGE_PATH" + "STORAGE_PATH_LOCAL" + # Database Configuration + "MYSQL_ROOT_PASSWORD" + "MYSQL_USER" + "MYSQL_PORT" + "MYSQL_HOST" + "DB_AUTH_NAME" + "DB_WORLD_NAME" + "DB_CHARACTERS_NAME" + "DB_PLAYERBOTS_NAME" + # Container Configuration + "CONTAINER_MYSQL" + "CONTAINER_USER" + ) + + log_info "Checking required variables..." + + for var in "${required_vars[@]}"; do + if check_var "$var"; then + log_success "$var=${!var}" + else + log_error "$var is not set" + missing+=("$var") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Missing required variables: ${missing[*]}" + return 1 + fi + + log_success "All required variables are set" + return 0 +} + +# Validate optional variables (strict mode) +validate_optional() { + local missing=() + + local optional_vars=( + # MySQL Performance Tuning + "MYSQL_INNODB_BUFFER_POOL_SIZE" + "MYSQL_INNODB_LOG_FILE_SIZE" + "MYSQL_INNODB_REDO_LOG_CAPACITY" + # Database Connection Settings + "DB_RECONNECT_SECONDS" + "DB_RECONNECT_ATTEMPTS" + # Build Configuration + "MODULES_REBUILD_SOURCE_PATH" + # Backup Configuration + "BACKUP_PATH" + "BACKUP_RETENTION_DAYS" + "BACKUP_RETENTION_HOURS" + # Image Configuration + "AC_AUTHSERVER_IMAGE" + "AC_WORLDSERVER_IMAGE" + "AC_DB_IMPORT_IMAGE" + ) + + log_info "Checking optional variables..." + + for var in "${optional_vars[@]}"; do + if check_var "$var"; then + log_success "$var is set" + else + log_warning "$var is not set (using default)" + missing+=("$var") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_warning "Optional variables not set: ${missing[*]}" + return 2 + fi + + log_success "All optional variables are set" + return 0 +} + +# Main validation +main() { + log_info "Validating environment configuration..." + echo "" + + # Check if .env exists + if [[ ! -f "$ENV_FILE" ]]; then + log_error ".env file not found at $ENV_FILE" + log_info "Copy .env.template to .env and configure it:" + log_info " cp $TEMPLATE_FILE $ENV_FILE" + exit 1 + fi + + # Load environment + if ! load_env "$ENV_FILE"; then + log_error "Failed to load $ENV_FILE" + exit 1 + fi + + log_success "Loaded environment from $ENV_FILE" + echo "" + + # Validate required variables + if ! validate_required; then + EXIT_CODE=1 + fi + + echo "" + + # Validate optional variables if strict mode + if $STRICT_MODE; then + if ! validate_optional; then + [[ $EXIT_CODE -eq 0 ]] && EXIT_CODE=2 + fi + echo "" + fi + + # Final summary + if [[ $EXIT_CODE -eq 0 ]]; then + log_success "Environment validation passed ✨" + elif [[ $EXIT_CODE -eq 1 ]]; then + log_error "Environment validation failed (missing required variables)" + elif [[ $EXIT_CODE -eq 2 ]]; then + log_warning "Environment validation passed with warnings (missing optional variables)" + fi + + exit $EXIT_CODE +} + +main "$@" diff --git a/scripts/go/go.mod b/scripts/go/go.mod index b337c68..9962054 100644 --- a/scripts/go/go.mod +++ b/scripts/go/go.mod @@ -1,4 +1,4 @@ -module acore-compose/statusdash +module azerothcore-realmmaster/statusdash go 1.22 diff --git a/scripts/python/update_module_manifest.py b/scripts/python/update_module_manifest.py index 8ef5880..b143312 100755 --- a/scripts/python/update_module_manifest.py +++ b/scripts/python/update_module_manifest.py @@ -46,7 +46,7 @@ CATEGORY_BY_TYPE = { "data": "data", "cpp": "uncategorized", } -USER_AGENT = "acore-compose-module-manifest" +USER_AGENT = "azerothcore-realmmaster-module-manifest" def parse_args(argv: Sequence[str]) -> argparse.Namespace: