diff --git a/.env.template b/.env.template index 204268d..1404fea 100644 --- a/.env.template +++ b/.env.template @@ -48,12 +48,12 @@ AC_DB_IMPORT_IMAGE=acore/ac-wotlk-db-import:14.0.0-dev AC_AUTHSERVER_IMAGE=acore/ac-wotlk-authserver:14.0.0-dev AC_WORLDSERVER_IMAGE=acore/ac-wotlk-worldserver:14.0.0-dev # Services (Playerbots) -AC_AUTHSERVER_IMAGE_PLAYERBOTS=acore/ac-wotlk-authserver:master -AC_WORLDSERVER_IMAGE_PLAYERBOTS=acore/ac-wotlk-worldserver:master +AC_AUTHSERVER_IMAGE_PLAYERBOTS=acore-compose:authserver-playerbots +AC_WORLDSERVER_IMAGE_PLAYERBOTS=acore-compose:worldserver-playerbots # Services (Module Build Tags) # Images used during module compilation and tagging -AC_AUTHSERVER_IMAGE_MODULES=acore/ac-wotlk-authserver:modules-latest -AC_WORLDSERVER_IMAGE_MODULES=acore/ac-wotlk-worldserver:modules-latest +AC_AUTHSERVER_IMAGE_MODULES=acore-compose:authserver-modules-latest +AC_WORLDSERVER_IMAGE_MODULES=acore-compose:worldserver-modules-latest # Client Data AC_CLIENT_DATA_IMAGE=acore/ac-wotlk-client-data:14.0.0-dev AC_CLIENT_DATA_IMAGE_PLAYERBOTS=uprightbass360/azerothcore-wotlk-playerbots:client-data-Playerbot diff --git a/README.md b/README.md index dcbd2bf..612a510 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ cd acore-compose ./deploy.sh ``` -> ℹ️ **Image Sources:** Vanilla/standard profiles run the upstream `acore/*` images. As soon as you enable playerbots or any C++ module, the toolchain switches to the `uprightbass360/azerothcore-wotlk-playerbots` fork, rebuilds it locally when needed, and produces fresh `uprightbass360/...:modules-latest` tags. +> ℹ️ **Image Sources:** Vanilla/standard profiles run the upstream `acore/*` images. As soon as you enable playerbots or any C++ module, the toolchain compiles locally and retags the results to your compose project name (for example, `acore-compose:authserver-playerbots`, `acore-compose:worldserver-playerbots`, `acore-compose:db-import-playerbots`, `acore-compose:client-data-playerbots`, and `acore-compose:authserver-modules-latest`), keeping everything self-contained. **4. Create Admin Account** @@ -83,7 +83,7 @@ set realmlist 203.0.113.100 8215 ### ✅ Core Server Components - **AzerothCore 3.3.5a** - WotLK server application - **MySQL 8.0** - Database with intelligent initialization and restoration -- **Smart Module System** - Automated module management and source builds (compiles the uprightbass360 playerbot fork whenever modules need C++ changes) +- **Smart Module System** - Automated module management and source builds (compiles the [mod-playerbots fork](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot) whenever modules need C++ changes) - **phpMyAdmin** - Web-based database administration - **Keira3** - Game content editor and developer tools @@ -219,7 +219,7 @@ ssh docker-server ' ./deploy.sh --yes --no-watch ' ``` - Because the `.env` now points the modules profile at the `uprightbass360/...:modules-latest` tags, the remote compose run uses the build you just migrated—no additional rebuild required. + Because the `.env` now points the modules profile at your project-local tags (for example `acore-compose:authserver-modules-latest`), the remote compose run uses the build you just migrated—no additional rebuild required. 4. **Verify** ```bash @@ -382,7 +382,7 @@ http://YOUR_SERVER_IP:4201 # Module staging and compilation ./scripts/stage-modules.sh # Download and stage enabled modules (preps upright playerbot builds) -./scripts/rebuild-with-modules.sh --yes # Rebuild uprightbass360/playerbot images with your modules +./scripts/rebuild-with-modules.sh --yes # Rebuild mod-playerbots images with your modules ./scripts/setup-source.sh # Initialize/update source repositories (auto-switches to playerbot fork for modules) # Module configuration management diff --git a/build.sh b/build.sh index 013c8f7..d1dd135 100755 --- a/build.sh +++ b/build.sh @@ -81,6 +81,20 @@ read_env(){ echo "$value" } +update_env_value(){ + local key="$1" value="$2" env_file="$ENV_PATH" + [ -n "$env_file" ] || return 0 + if [ ! -f "$env_file" ]; then + printf '%s=%s\n' "$key" "$value" >> "$env_file" + return 0 + fi + if grep -q "^${key}=" "$env_file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$env_file" + else + printf '\n%s=%s\n' "$key" "$value" >> "$env_file" + fi +} + MODULE_HELPER="$ROOT_DIR/scripts/modules.py" MODULE_STATE_INITIALIZED=0 declare -a MODULES_COMPILE_LIST=() @@ -99,6 +113,7 @@ generate_module_state(){ local storage_root storage_root="$(resolve_local_storage_path)" local output_dir="${storage_root}/modules" + ensure_modules_dir_writable "$storage_root" if ! python3 "$MODULE_HELPER" --env-path "$ENV_PATH" --manifest "$ROOT_DIR/config/modules.json" generate --output-dir "$output_dir"; then err "Module manifest validation failed. See errors above." exit 1 @@ -229,8 +244,8 @@ detect_rebuild_reasons(){ if [ "$any_cxx_modules" = "1" ]; then local authserver_modules_image local worldserver_modules_image - authserver_modules_image="$(read_env AC_AUTHSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:authserver-modules-latest")" - worldserver_modules_image="$(read_env AC_WORLDSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest")" + authserver_modules_image="$(read_env AC_AUTHSERVER_IMAGE_MODULES "$(resolve_project_image "authserver-modules-latest")")" + worldserver_modules_image="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(resolve_project_image "worldserver-modules-latest")")" if ! docker image inspect "$authserver_modules_image" >/dev/null 2>&1; then reasons+=("C++ modules enabled but authserver modules image $authserver_modules_image is missing") @@ -336,6 +351,41 @@ resolve_project_name(){ echo "$sanitized" } +ensure_modules_dir_writable(){ + local base_path="$1" + local modules_dir="${base_path%/}/modules" + ensure_host_writable "$modules_dir" +} + +ensure_host_writable(){ + local target="$1" + [ -n "$target" ] || return 0 + if [ -d "$target" ] || mkdir -p "$target" 2>/dev/null; then + local uid gid + uid="$(id -u)" + gid="$(id -g)" + if ! chown -R "$uid":"$gid" "$target" 2>/dev/null; then + if command -v docker >/dev/null 2>&1; then + local helper_image + helper_image="$(read_env ALPINE_IMAGE "alpine:latest")" + docker run --rm \ + -u 0:0 \ + -v "$target":/workspace \ + "$helper_image" \ + sh -c "chown -R ${uid}:${gid} /workspace" >/dev/null 2>&1 || true + fi + fi + chmod -R u+rwX "$target" 2>/dev/null || true + fi +} + +resolve_project_image(){ + local tag="$1" + local project_name + project_name="$(resolve_project_name)" + echo "${project_name}:${tag}" +} + stage_modules(){ local src_path="$1" local storage_path @@ -355,17 +405,21 @@ stage_modules(){ local local_modules_dir="${src_path}/modules" mkdir -p "$local_modules_dir" + ensure_host_writable "$local_modules_dir" local staging_modules_dir="${storage_path}/modules" export MODULES_HOST_DIR="$staging_modules_dir" + ensure_host_writable "$staging_modules_dir" local env_target_dir="$src_path/env/dist/etc" mkdir -p "$env_target_dir" export MODULES_ENV_TARGET_DIR="$env_target_dir" + ensure_host_writable "$env_target_dir" local lua_target_dir="$src_path/lua_scripts" mkdir -p "$lua_target_dir" export MODULES_LUA_TARGET_DIR="$lua_target_dir" + ensure_host_writable "$lua_target_dir" # Set up local storage path for build sentinel tracking local local_storage_path @@ -475,21 +529,31 @@ tag_module_images(){ local target_auth local target_world - source_auth="$(read_env AC_AUTHSERVER_IMAGE_PLAYERBOTS "uprightbass360/azerothcore-wotlk-playerbots:authserver-Playerbot")" - source_world="$(read_env AC_WORLDSERVER_IMAGE_PLAYERBOTS "uprightbass360/azerothcore-wotlk-playerbots:worldserver-Playerbot")" - target_auth="$(read_env AC_AUTHSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:authserver-modules-latest")" - target_world="$(read_env AC_WORLDSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest")" + source_auth="$(read_env AC_AUTHSERVER_IMAGE_PLAYERBOTS "$(resolve_project_image "authserver-playerbots")")" + source_world="$(read_env AC_WORLDSERVER_IMAGE_PLAYERBOTS "$(resolve_project_image "worldserver-playerbots")")" + target_auth="$(read_env AC_AUTHSERVER_IMAGE_MODULES "$(resolve_project_image "authserver-modules-latest")")" + target_world="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(resolve_project_image "worldserver-modules-latest")")" if docker image inspect "$source_auth" >/dev/null 2>&1; then - docker tag "$source_auth" "$target_auth" - ok "Tagged $target_auth from $source_auth" + if docker tag "$source_auth" "$target_auth"; then + ok "Tagged $target_auth from $source_auth" + update_env_value "AC_AUTHSERVER_IMAGE_PLAYERBOTS" "$source_auth" + update_env_value "AC_AUTHSERVER_IMAGE_MODULES" "$target_auth" + else + warn "Failed to tag $target_auth from $source_auth" + fi else warn "Source authserver image $source_auth not found; skipping modules tag" fi if docker image inspect "$source_world" >/dev/null 2>&1; then - docker tag "$source_world" "$target_world" - ok "Tagged $target_world from $source_world" + if docker tag "$source_world" "$target_world"; then + ok "Tagged $target_world from $source_world" + update_env_value "AC_WORLDSERVER_IMAGE_PLAYERBOTS" "$source_world" + update_env_value "AC_WORLDSERVER_IMAGE_MODULES" "$target_world" + else + warn "Failed to tag $target_world from $source_world" + fi else warn "Source worldserver image $source_world not found; skipping modules tag" fi diff --git a/cleanup.sh b/cleanup.sh index 1d722e0..c616a2a 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -128,6 +128,22 @@ STORAGE_PATH="${STORAGE_PATH:-$STORAGE_PATH_DEFAULT}" STORAGE_PATH_LOCAL="${STORAGE_PATH_LOCAL:-$STORAGE_PATH_LOCAL_DEFAULT}" PROJECT_NAME="${COMPOSE_PROJECT_NAME:-ac-compose}" +sanitize_project_name(){ + local raw="$1" + local sanitized + sanitized="$(echo "$raw" | tr '[:upper:]' '[:lower:]')" + sanitized="${sanitized// /-}" + sanitized="$(echo "$sanitized" | tr -cd 'a-z0-9_-')" + if [[ -z "$sanitized" ]]; then + sanitized="acore-compose" + elif [[ ! "$sanitized" =~ ^[a-z0-9] ]]; then + sanitized="ac${sanitized}" + fi + echo "$sanitized" +} + +PROJECT_IMAGE_PREFIX="$(sanitize_project_name "${COMPOSE_PROJECT_NAME:-acore-compose}")" + remove_storage_dir(){ local path="$1" if [ -d "$path" ]; then @@ -203,7 +219,8 @@ 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 playerbots images" "docker images --format '{{.Repository}}:{{.Tag}}' | grep -E '^uprightbass360/azerothcore-wotlk-playerbots' | 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 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/deploy.sh b/deploy.sh index db0cb49..9fb67da 100755 --- a/deploy.sh +++ b/deploy.sh @@ -288,6 +288,28 @@ resolve_local_storage_path(){ echo "${path%/}" } +ensure_modules_dir_writable(){ + local base_path="$1" + local modules_dir="${base_path%/}/modules" + if [ -d "$modules_dir" ] || mkdir -p "$modules_dir" 2>/dev/null; then + local uid gid + uid="$(id -u)" + gid="$(id -g)" + if ! chown -R "$uid":"$gid" "$modules_dir" 2>/dev/null; then + if command -v docker >/dev/null 2>&1; then + local helper_image + helper_image="$(read_env ALPINE_IMAGE "alpine:latest")" + docker run --rm \ + -u 0:0 \ + -v "$modules_dir":/modules \ + "$helper_image" \ + sh -c "chown -R ${uid}:${gid} /modules && chmod -R ug+rwX /modules" >/dev/null 2>&1 || true + fi + fi + chmod -R u+rwX "$modules_dir" 2>/dev/null || true + fi +} + ensure_module_state(){ if [ "$MODULE_STATE_INITIALIZED" -eq 1 ]; then return @@ -296,6 +318,7 @@ ensure_module_state(){ local storage_root storage_root="$(resolve_local_storage_path)" local output_dir="${storage_root}/modules" + ensure_modules_dir_writable "$storage_root" if ! python3 "$MODULE_HELPER" --env-path "$ENV_PATH" --manifest "$ROOT_DIR/config/modules.json" generate --output-dir "$output_dir"; then err "Module manifest validation failed. See errors above." @@ -329,6 +352,13 @@ resolve_project_name(){ echo "$sanitized" } +resolve_project_image(){ + local tag="$1" + local project_name + project_name="$(resolve_project_name)" + echo "${project_name}:${tag}" +} + filter_empty_lines(){ awk ' /^[[:space:]]*$/ { @@ -369,8 +399,8 @@ detect_build_needed(){ if [ "$any_cxx_modules" = "1" ]; then local authserver_modules_image local worldserver_modules_image - authserver_modules_image="$(read_env AC_AUTHSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:authserver-modules-latest")" - worldserver_modules_image="$(read_env AC_WORLDSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest")" + authserver_modules_image="$(read_env AC_AUTHSERVER_IMAGE_MODULES "$(resolve_project_image "authserver-modules-latest")")" + worldserver_modules_image="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(resolve_project_image "worldserver-modules-latest")")" if ! docker image inspect "$authserver_modules_image" >/dev/null 2>&1; then reasons+=("C++ modules enabled but authserver modules image $authserver_modules_image is missing") @@ -412,7 +442,25 @@ mark_deployment_complete(){ warn "Cannot create local-storage directory. Deployment tracking may not work properly." return 0 fi - date > "$sentinel" + if ! date > "$sentinel" 2>/dev/null; then + local sentinel_dir + sentinel_dir="$(dirname "$sentinel")" + if command -v docker >/dev/null 2>&1; then + local helper_image + helper_image="$(read_env ALPINE_IMAGE "alpine:latest")" + local container_user + container_user="$(read_env CONTAINER_USER "$(id -u):$(id -g)")" + docker run --rm \ + --user "$container_user" \ + -v "$sentinel_dir":/sentinel \ + "$helper_image" \ + sh -c 'date > /sentinel/.last_deployed' >/dev/null 2>&1 || true + fi + if [ ! -f "$sentinel" ]; then + warn "Unable to update deployment marker at $sentinel (permission denied)." + return 0 + fi + fi } modules_need_rebuild(){ @@ -453,6 +501,17 @@ prompt_build_if_needed(){ fi fi + if [ "$ASSUME_YES" -eq 1 ]; then + warn "Build required; auto-confirming (--yes)" + if (cd "$ROOT_DIR" && ./build.sh --yes); then + ok "Build completed successfully" + return 0 + else + err "Build failed" + return 1 + fi + fi + # Interactive prompt echo warn "Build appears to be required:" diff --git a/scripts/manage-modules.sh b/scripts/manage-modules.sh index 658ed37..b02eb29 100755 --- a/scripts/manage-modules.sh +++ b/scripts/manage-modules.sh @@ -18,6 +18,27 @@ ok(){ printf '%b\n' "${GREEN}✅ $*${NC}"; } warn(){ printf '%b\n' "${YELLOW}⚠️ $*${NC}"; } err(){ printf '%b\n' "${RED}❌ $*${NC}"; exit 1; } +read_env_value(){ + local key="$1" default="${2:-}" value="${!key:-}" + if [ -n "$value" ]; then + echo "$value" + return + fi + if [ -f "$ENV_PATH" ]; then + value="$(grep -E "^${key}=" "$ENV_PATH" 2>/dev/null | tail -n1 | cut -d'=' -f2- | tr -d '\r')" + value="$(echo "$value" | sed 's/[[:space:]]*#.*//' | sed 's/[[:space:]]*$//')" + if [[ "$value" == \"*\" && "$value" == *\" ]]; then + value="${value:1:-1}" + elif [[ "$value" == \'*\' && "$value" == *\' ]]; then + value="${value:1:-1}" + fi + fi + if [ -z "${value:-}" ]; then + value="$default" + fi + printf '%s\n' "${value}" +} + ensure_python(){ if ! command -v python3 >/dev/null 2>&1; then err "python3 is required but not installed in PATH" @@ -193,11 +214,24 @@ update_playerbots_db_info(){ return 0 fi - local host="${CONTAINER_MYSQL:-${MYSQL_HOST:-127.0.0.1}}" - local port="${MYSQL_PORT:-3306}" - local user="${MYSQL_USER:-root}" - local pass="${MYSQL_ROOT_PASSWORD:-acore}" - local db="${DB_PLAYERBOTS_NAME:-acore_playerbots}" + local host + host="$(read_env_value CONTAINER_MYSQL)" + if [ -z "$host" ]; then + host="$(read_env_value MYSQL_HOST)" + fi + host="${host:-ac-mysql}" + + local port + port="$(read_env_value MYSQL_PORT "3306")" + + local user + user="$(read_env_value MYSQL_USER "root")" + + local pass + pass="$(read_env_value MYSQL_ROOT_PASSWORD)" + + local db + db="$(read_env_value DB_PLAYERBOTS_NAME "acore_playerbots")" local value="${host};${port};${user};${pass};${db}" if grep -qE '^[[:space:]]*PlayerbotsDatabaseInfo[[:space:]]*=' "$target"; then @@ -396,6 +430,16 @@ track_module_state(){ rm -f "$host_rebuild_sentinel" 2>/dev/null || true fi fi + + if [ "${MODULES_LOCAL_RUN:-0}" = "1" ]; then + local target_dir="${MODULES_HOST_DIR:-$(pwd)}" + local desired_user + desired_user="$(id -u):$(id -g)" + if [ -d "$target_dir" ]; then + chown -R "$desired_user" "$target_dir" >/dev/null 2>&1 || true + chmod -R ug+rwX "$target_dir" >/dev/null 2>&1 || true + fi + fi } main(){ diff --git a/scripts/migrate-stack.sh b/scripts/migrate-stack.sh index 52eaf19..5d19c3e 100755 --- a/scripts/migrate-stack.sh +++ b/scripts/migrate-stack.sh @@ -5,6 +5,43 @@ set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +ENV_FILE="$PROJECT_ROOT/.env" + +read_env_value(){ + local key="$1" default="$2" value="${!key:-}" + if [ -z "$value" ] && [ -f "$ENV_FILE" ]; then + value="$(grep -E "^${key}=" "$ENV_FILE" 2>/dev/null | tail -n1 | cut -d'=' -f2- | tr -d '\r')" + fi + if [ -z "$value" ]; then + value="$default" + fi + echo "$value" +} + +resolve_project_name(){ + local raw_name + raw_name="$(read_env_value COMPOSE_PROJECT_NAME "acore-compose")" + local sanitized + sanitized="$(echo "$raw_name" | tr '[:upper:]' '[:lower:]')" + sanitized="${sanitized// /-}" + sanitized="$(echo "$sanitized" | tr -cd 'a-z0-9_-')" + if [[ -z "$sanitized" ]]; then + sanitized="acore-compose" + elif [[ ! "$sanitized" =~ ^[a-z0-9] ]]; then + sanitized="ac${sanitized}" + fi + echo "$sanitized" +} + +resolve_project_image(){ + local tag="$1" + local project_name + project_name="$(resolve_project_name)" + echo "${project_name}:${tag}" +} + usage(){ cat <<'EOF_HELP' Usage: $(basename "$0") --host HOST --user USER [options] @@ -57,7 +94,10 @@ fi PROJECT_DIR="${PROJECT_DIR:-/home/${USER}/acore-compose}" REMOTE_STORAGE="${REMOTE_STORAGE:-${PROJECT_DIR}/storage}" -LOCAL_STORAGE_ROOT="${STORAGE_PATH_LOCAL:-./local-storage}" +LOCAL_STORAGE_ROOT="${STORAGE_PATH_LOCAL:-}" +if [ -z "$LOCAL_STORAGE_ROOT" ]; then + LOCAL_STORAGE_ROOT="$(read_env_value STORAGE_PATH_LOCAL "./local-storage")" +fi LOCAL_STORAGE_ROOT="${LOCAL_STORAGE_ROOT%/}" [ -z "$LOCAL_STORAGE_ROOT" ] && LOCAL_STORAGE_ROOT="." TARBALL="${TARBALL:-${LOCAL_STORAGE_ROOT}/images/acore-modules-images.tar}" @@ -176,29 +216,24 @@ mkdir -p "$(dirname "$TARBALL")" # Check which images are available and collect them IMAGES_TO_SAVE=() -# Check for custom module images (built by build.sh) -if docker image inspect uprightbass360/azerothcore-wotlk-playerbots:authserver-modules-latest >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(uprightbass360/azerothcore-wotlk-playerbots:authserver-modules-latest) -fi -if docker image inspect uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest) -fi +project_auth_modules="$(resolve_project_image "authserver-modules-latest")" +project_world_modules="$(resolve_project_image "worldserver-modules-latest")" +project_auth_playerbots="$(resolve_project_image "authserver-playerbots")" +project_world_playerbots="$(resolve_project_image "worldserver-playerbots")" +project_db_import="$(resolve_project_image "db-import-playerbots")" +project_client_data="$(resolve_project_image "client-data-playerbots")" -# Check for pre-compiled playerbots images -if docker image inspect uprightbass360/azerothcore-wotlk-playerbots:worldserver-Playerbot >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(uprightbass360/azerothcore-wotlk-playerbots:worldserver-Playerbot) -fi -if docker image inspect uprightbass360/azerothcore-wotlk-playerbots:authserver-Playerbot >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(uprightbass360/azerothcore-wotlk-playerbots:authserver-Playerbot) -fi - -# Check for standard AzerothCore images (fallback) -if docker image inspect acore/ac-wotlk-worldserver:modules-latest >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(acore/ac-wotlk-worldserver:modules-latest) -fi -if docker image inspect acore/ac-wotlk-authserver:modules-latest >/dev/null 2>&1; then - IMAGES_TO_SAVE+=(acore/ac-wotlk-authserver:modules-latest) -fi +for image in \ + "$project_auth_modules" \ + "$project_world_modules" \ + "$project_auth_playerbots" \ + "$project_world_playerbots" \ + "$project_db_import" \ + "$project_client_data"; do + if docker image inspect "$image" >/dev/null 2>&1; then + IMAGES_TO_SAVE+=("$image") + fi +done if [ ${#IMAGES_TO_SAVE[@]} -eq 0 ]; then echo "❌ No AzerothCore images found to migrate. Run './build.sh' first or pull standard images." diff --git a/scripts/rebuild-with-modules.sh b/scripts/rebuild-with-modules.sh index d46026d..d84afb8 100755 --- a/scripts/rebuild-with-modules.sh +++ b/scripts/rebuild-with-modules.sh @@ -44,6 +44,80 @@ read_env(){ echo "$value" } +update_env_value(){ + local key="$1" value="$2" env_file="$ENV_FILE" + [ -n "$env_file" ] || return 0 + if [ ! -f "$env_file" ]; then + printf '%s=%s\n' "$key" "$value" >> "$env_file" + return 0 + fi + if grep -q "^${key}=" "$env_file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$env_file" + else + printf '\n%s=%s\n' "$key" "$value" >> "$env_file" + fi +} + +find_image_with_suffix(){ + local suffix="$1" + docker images --format '{{.Repository}}:{{.Tag}}' | grep -E ":${suffix}$" | head -n1 +} + +cleanup_legacy_tags(){ + local suffix="$1" keep_tag="$2" + docker images --format '{{.Repository}}:{{.Tag}}' | grep -E ":${suffix}$" | while read -r tag; do + [ "$tag" = "$keep_tag" ] && continue + docker rmi "$tag" >/dev/null 2>&1 || true + done +} + +ensure_project_image_tag(){ + local suffix="$1" target="$2" + if [ -n "$target" ] && docker image inspect "$target" >/dev/null 2>&1; then + cleanup_legacy_tags "$suffix" "$target" + echo "$target" + return 0 + fi + local source + source="$(find_image_with_suffix "$suffix")" + if [ -z "$source" ]; then + echo "" + return 1 + fi + if docker tag "$source" "$target" >/dev/null 2>&1; then + if [ "$source" != "$target" ]; then + docker rmi "$source" >/dev/null 2>&1 || true + fi + cleanup_legacy_tags "$suffix" "$target" + echo "$target" + return 0 + fi + echo "" + return 1 +} + +resolve_project_name(){ + local raw_name + raw_name="$(read_env COMPOSE_PROJECT_NAME "acore-compose")" + local sanitized + sanitized="$(echo "$raw_name" | tr '[:upper:]' '[:lower:]')" + sanitized="${sanitized// /-}" + sanitized="$(echo "$sanitized" | tr -cd 'a-z0-9_-')" + if [[ -z "$sanitized" ]]; then + sanitized="acore-compose" + elif [[ ! "$sanitized" =~ ^[a-z0-9] ]]; then + sanitized="ac${sanitized}" + fi + echo "$sanitized" +} + +resolve_project_image(){ + local tag="$1" + local project_name + project_name="$(resolve_project_name)" + echo "${project_name}:${tag}" +} + default_source_path(){ local require_playerbot require_playerbot="$(modules_require_playerbot_source)" @@ -283,21 +357,67 @@ get_template_value() { TARGET_AUTHSERVER_IMAGE="$(read_env AC_AUTHSERVER_IMAGE_MODULES "$(get_template_value "AC_AUTHSERVER_IMAGE_MODULES")")" TARGET_WORLDSERVER_IMAGE="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(get_template_value "AC_WORLDSERVER_IMAGE_MODULES")")" - PLAYERBOTS_AUTHSERVER_IMAGE="$(read_env AC_AUTHSERVER_IMAGE_PLAYERBOTS "$(get_template_value "AC_AUTHSERVER_IMAGE_PLAYERBOTS")")" PLAYERBOTS_WORLDSERVER_IMAGE="$(read_env AC_WORLDSERVER_IMAGE_PLAYERBOTS "$(get_template_value "AC_WORLDSERVER_IMAGE_PLAYERBOTS")")" -echo "🔁 Tagging modules images from playerbot build artifacts" -if docker image inspect "$PLAYERBOTS_AUTHSERVER_IMAGE" >/dev/null 2>&1; then - docker tag "$PLAYERBOTS_AUTHSERVER_IMAGE" "$TARGET_AUTHSERVER_IMAGE" +[ -z "$TARGET_AUTHSERVER_IMAGE" ] && TARGET_AUTHSERVER_IMAGE="$(resolve_project_image "authserver-modules-latest")" +[ -z "$TARGET_WORLDSERVER_IMAGE" ] && TARGET_WORLDSERVER_IMAGE="$(resolve_project_image "worldserver-modules-latest")" +[ -z "$PLAYERBOTS_AUTHSERVER_IMAGE" ] && PLAYERBOTS_AUTHSERVER_IMAGE="$(resolve_project_image "authserver-playerbots")" +[ -z "$PLAYERBOTS_WORLDSERVER_IMAGE" ] && PLAYERBOTS_WORLDSERVER_IMAGE="$(resolve_project_image "worldserver-playerbots")" + +PLAYERBOTS_AUTHSERVER_IMAGE="$(ensure_project_image_tag "authserver-Playerbot" "$(resolve_project_image "authserver-playerbots")")" +if [ -z "$PLAYERBOTS_AUTHSERVER_IMAGE" ]; then + echo "⚠️ Warning: unable to ensure project tag for authserver playerbots image" else - echo "⚠️ Warning: $PLAYERBOTS_AUTHSERVER_IMAGE not found, skipping authserver tag" + update_env_value "AC_AUTHSERVER_IMAGE_PLAYERBOTS" "$PLAYERBOTS_AUTHSERVER_IMAGE" fi -if docker image inspect "$PLAYERBOTS_WORLDSERVER_IMAGE" >/dev/null 2>&1; then - docker tag "$PLAYERBOTS_WORLDSERVER_IMAGE" "$TARGET_WORLDSERVER_IMAGE" +PLAYERBOTS_WORLDSERVER_IMAGE="$(ensure_project_image_tag "worldserver-Playerbot" "$(resolve_project_image "worldserver-playerbots")")" +if [ -z "$PLAYERBOTS_WORLDSERVER_IMAGE" ]; then + echo "⚠️ Warning: unable to ensure project tag for worldserver playerbots image" else - echo "⚠️ Warning: $PLAYERBOTS_WORLDSERVER_IMAGE not found, skipping worldserver tag" + update_env_value "AC_WORLDSERVER_IMAGE_PLAYERBOTS" "$PLAYERBOTS_WORLDSERVER_IMAGE" +fi + +echo "🔁 Tagging modules images from playerbot build artifacts" +if [ -n "$PLAYERBOTS_AUTHSERVER_IMAGE" ] && docker image inspect "$PLAYERBOTS_AUTHSERVER_IMAGE" >/dev/null 2>&1; then + if docker tag "$PLAYERBOTS_AUTHSERVER_IMAGE" "$TARGET_AUTHSERVER_IMAGE"; then + echo "✅ Tagged $TARGET_AUTHSERVER_IMAGE from $PLAYERBOTS_AUTHSERVER_IMAGE" + update_env_value "AC_AUTHSERVER_IMAGE_PLAYERBOTS" "$PLAYERBOTS_AUTHSERVER_IMAGE" + update_env_value "AC_AUTHSERVER_IMAGE_MODULES" "$TARGET_AUTHSERVER_IMAGE" + else + echo "⚠️ Failed to tag $TARGET_AUTHSERVER_IMAGE from $PLAYERBOTS_AUTHSERVER_IMAGE" + fi +else + echo "⚠️ Warning: unable to locate project-tagged authserver playerbots image" +fi + +if [ -n "$PLAYERBOTS_WORLDSERVER_IMAGE" ] && docker image inspect "$PLAYERBOTS_WORLDSERVER_IMAGE" >/dev/null 2>&1; then + if docker tag "$PLAYERBOTS_WORLDSERVER_IMAGE" "$TARGET_WORLDSERVER_IMAGE"; then + echo "✅ Tagged $TARGET_WORLDSERVER_IMAGE from $PLAYERBOTS_WORLDSERVER_IMAGE" + update_env_value "AC_WORLDSERVER_IMAGE_PLAYERBOTS" "$PLAYERBOTS_WORLDSERVER_IMAGE" + update_env_value "AC_WORLDSERVER_IMAGE_MODULES" "$TARGET_WORLDSERVER_IMAGE" + else + echo "⚠️ Failed to tag $TARGET_WORLDSERVER_IMAGE from $PLAYERBOTS_WORLDSERVER_IMAGE" + fi +else + echo "⚠️ Warning: unable to locate project-tagged worldserver playerbots image" +fi + +TARGET_DB_IMPORT_IMAGE="$(resolve_project_image "db-import-playerbots")" +DB_IMPORT_IMAGE="$(ensure_project_image_tag "db-import-Playerbot" "$TARGET_DB_IMPORT_IMAGE")" +if [ -n "$DB_IMPORT_IMAGE" ]; then + update_env_value "AC_DB_IMPORT_IMAGE" "$DB_IMPORT_IMAGE" +else + echo "⚠️ Warning: unable to ensure project tag for db-import image" +fi + +TARGET_CLIENT_DATA_IMAGE="$(resolve_project_image "client-data-playerbots")" +CLIENT_DATA_IMAGE="$(ensure_project_image_tag "client-data-Playerbot" "$TARGET_CLIENT_DATA_IMAGE")" +if [ -n "$CLIENT_DATA_IMAGE" ]; then + update_env_value "AC_CLIENT_DATA_IMAGE_PLAYERBOTS" "$CLIENT_DATA_IMAGE" +else + echo "⚠️ Warning: unable to ensure project tag for client-data image" fi show_rebuild_step 5 5 "Cleaning up build containers" diff --git a/scripts/setup-source.sh b/scripts/setup-source.sh index eabf4e6..1442daa 100755 --- a/scripts/setup-source.sh +++ b/scripts/setup-source.sh @@ -69,7 +69,7 @@ fi ACORE_REPO_STANDARD="${ACORE_REPO_STANDARD:-https://github.com/azerothcore/azerothcore-wotlk.git}" ACORE_BRANCH_STANDARD="${ACORE_BRANCH_STANDARD:-master}" -ACORE_REPO_PLAYERBOTS="${ACORE_REPO_PLAYERBOTS:-https://github.com/uprightbass360/azerothcore-wotlk-playerbots.git}" +ACORE_REPO_PLAYERBOTS="${ACORE_REPO_PLAYERBOTS:-https://github.com/mod-playerbots/azerothcore-wotlk.git}" ACORE_BRANCH_PLAYERBOTS="${ACORE_BRANCH_PLAYERBOTS:-Playerbot}" # Repository and branch selection based on playerbots mode diff --git a/scripts/stage-modules.sh b/scripts/stage-modules.sh index 79cbac5..baa2d9f 100755 --- a/scripts/stage-modules.sh +++ b/scripts/stage-modules.sh @@ -97,6 +97,28 @@ read_env(){ echo "$value" } +resolve_project_name(){ + local raw_name + raw_name="$(read_env COMPOSE_PROJECT_NAME "acore-compose")" + local sanitized + sanitized="$(echo "$raw_name" | tr '[:upper:]' '[:lower:]')" + sanitized="${sanitized// /-}" + sanitized="$(echo "$sanitized" | tr -cd 'a-z0-9_-')" + if [[ -z "$sanitized" ]]; then + sanitized="acore-compose" + elif [[ ! "$sanitized" =~ ^[a-z0-9] ]]; then + sanitized="ac${sanitized}" + fi + echo "$sanitized" +} + +resolve_project_image(){ + local tag="$1" + local project_name + project_name="$(resolve_project_name)" + echo "${project_name}:${tag}" +} + canonical_path(){ local path="$1" if command -v realpath >/dev/null 2>&1; then @@ -265,7 +287,7 @@ echo "🎯 Target profile: services-$TARGET_PROFILE" # Check if source rebuild is needed for modules profile REBUILD_NEEDED=0 -TARGET_WORLDSERVER_IMAGE_MODULES="$(read_env AC_WORLDSERVER_IMAGE_MODULES "uprightbass360/azerothcore-wotlk-playerbots:worldserver-modules-latest")" +TARGET_WORLDSERVER_IMAGE_MODULES="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(resolve_project_image "worldserver-modules-latest")")" if [ "$TARGET_PROFILE" = "modules" ]; then # Check if source image exists if ! docker image inspect "$TARGET_WORLDSERVER_IMAGE_MODULES" >/dev/null 2>&1; then diff --git a/setup.sh b/setup.sh index f90f899..e1d0054 100755 --- a/setup.sh +++ b/setup.sh @@ -43,6 +43,25 @@ get_template_value() { echo "$value" } +sanitize_project_name(){ + local raw="$1" + local sanitized + sanitized="$(echo "$raw" | tr '[:upper:]' '[:lower:]')" + sanitized="${sanitized// /-}" + sanitized="$(echo "$sanitized" | tr -cd 'a-z0-9_-')" + if [[ -z "$sanitized" ]]; then + sanitized="acore-compose" + elif [[ ! "$sanitized" =~ ^[a-z0-9] ]]; then + sanitized="ac${sanitized}" + fi + echo "$sanitized" +} + +resolve_project_image_tag(){ + local project="$1" tag="$2" + echo "${project}:${tag}" +} + declare -A TEMPLATE_VALUE_MAP=( [DEFAULT_MYSQL_PASSWORD]=MYSQL_ROOT_PASSWORD [DEFAULT_REALM_PORT]=WORLD_EXTERNAL_PORT @@ -905,6 +924,8 @@ fi local AC_WORLDSERVER_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_WORLD_IMAGE_PLAYERBOTS" local AC_AUTHSERVER_IMAGE_MODULES_VALUE="$DEFAULT_AUTH_IMAGE_MODULES" local AC_WORLDSERVER_IMAGE_MODULES_VALUE="$DEFAULT_WORLD_IMAGE_MODULES" + local AC_CLIENT_DATA_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_CLIENT_DATA_IMAGE_PLAYERBOTS" + local AC_DB_IMPORT_IMAGE_VALUE="$DEFAULT_AC_DB_IMPORT_IMAGE" local mod_var for mod_var in "${!MODULE_ENABLE_SET[@]}"; do @@ -1040,11 +1061,6 @@ fi export NEEDS_CXX_REBUILD - if [ "$MODULE_PLAYERBOTS" = "1" ]; then - AC_AUTHSERVER_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_AUTH_IMAGE_PLAYERBOTS" - AC_WORLDSERVER_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_WORLD_IMAGE_PLAYERBOTS" - fi - local SUMMARY_MODE_TEXT="$module_mode_label" if [ -z "$SUMMARY_MODE_TEXT" ]; then SUMMARY_MODE_TEXT="$MODE_SELECTION" @@ -1186,7 +1202,23 @@ fi MODULE_ELUNA=${MODULE_ELUNA:-$DEFAULT_MODULE_ELUNA} BACKUP_PATH=${BACKUP_PATH:-$DEFAULT_BACKUP_PATH} - { + local project_image_prefix + project_image_prefix="$(sanitize_project_name "$DEFAULT_COMPOSE_PROJECT_NAME")" + if [ "$MODULE_PLAYERBOTS" = "1" ] || [ "$NEEDS_CXX_REBUILD" = "1" ]; then + AC_AUTHSERVER_IMAGE_PLAYERBOTS_VALUE="$(resolve_project_image_tag "$project_image_prefix" "authserver-playerbots")" + AC_WORLDSERVER_IMAGE_PLAYERBOTS_VALUE="$(resolve_project_image_tag "$project_image_prefix" "worldserver-playerbots")" + AC_DB_IMPORT_IMAGE_VALUE="$(resolve_project_image_tag "$project_image_prefix" "db-import-playerbots")" + AC_CLIENT_DATA_IMAGE_PLAYERBOTS_VALUE="$(resolve_project_image_tag "$project_image_prefix" "client-data-playerbots")" + else + AC_AUTHSERVER_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_AUTH_IMAGE_PLAYERBOTS" + AC_WORLDSERVER_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_WORLD_IMAGE_PLAYERBOTS" + AC_DB_IMPORT_IMAGE_VALUE="$DEFAULT_AC_DB_IMPORT_IMAGE" + AC_CLIENT_DATA_IMAGE_PLAYERBOTS_VALUE="$DEFAULT_CLIENT_DATA_IMAGE_PLAYERBOTS" + fi + AC_AUTHSERVER_IMAGE_MODULES_VALUE="$(resolve_project_image_tag "$project_image_prefix" "authserver-modules-latest")" + AC_WORLDSERVER_IMAGE_MODULES_VALUE="$(resolve_project_image_tag "$project_image_prefix" "worldserver-modules-latest")" + +{ cat <