From 622fd518d298a40ccd7b1691c58e57775559867c Mon Sep 17 00:00:00 2001 From: uprightbass360 Date: Sat, 8 Nov 2025 01:49:21 -0500 Subject: [PATCH] Introduce dynamic overrides and rename module manifest --- .env.template | 17 ++- README.md | 34 ++++-- build.sh | 2 +- cleanup.sh | 12 +-- compose-overrides/mysql-expose.yml | 12 +++ .../worldserver-debug-logging.yml | 15 +++ config/{modules.json => module-manifest.json} | 0 .../module-profiles/RealmMaster.json | 37 ++++--- .../module-profiles}/all-modules.json | 0 .../module-profiles}/playerbots-only.json | 0 .../playerbots-suggested-modules.json | 0 .../module-profiles}/suggested-modules.json | 0 deploy.sh | 15 +-- docker-compose.mysql-expose.yml | 5 - docker-compose.yml | 2 +- docs/installing-azerothcore-with-docker.md | 20 +++- scripts/check_module_staging.py | 2 +- scripts/deploy-tools.sh | 11 +- scripts/hooks/REFACTORING_SUMMARY.md | 49 --------- scripts/lib/compose_overrides.sh | 101 ++++++++++++++++++ scripts/manage-modules-sql.sh | 2 +- scripts/manage-modules.sh | 8 +- scripts/modules.py | 6 +- scripts/rebuild-with-modules.sh | 4 +- scripts/stage-modules.sh | 12 +-- scripts/start-containers.sh | 2 +- scripts/verify-deployment.sh | 17 ++- setup.sh | 27 +++-- status.sh | 4 +- 29 files changed, 261 insertions(+), 155 deletions(-) create mode 100644 compose-overrides/mysql-expose.yml create mode 100644 compose-overrides/worldserver-debug-logging.yml rename config/{modules.json => module-manifest.json} (100%) rename profiles/sam.json => config/module-profiles/RealmMaster.json (68%) rename {profiles => config/module-profiles}/all-modules.json (100%) rename {profiles => config/module-profiles}/playerbots-only.json (100%) rename {profiles => config/module-profiles}/playerbots-suggested-modules.json (100%) rename {profiles => config/module-profiles}/suggested-modules.json (100%) delete mode 100644 docker-compose.mysql-expose.yml delete mode 100644 scripts/hooks/REFACTORING_SUMMARY.md create mode 100644 scripts/lib/compose_overrides.sh diff --git a/.env.template b/.env.template index 438b088..fc97510 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,14 @@ # Docker Compose will auto-load .env in the same folder as docker-compose.yml. # Template for acore-compose profiles-based compose +# ===================== +# Compose overrides (set to 1 to include matching file under compose-overrides/) +# ===================== +# mysql-expose.yml -> exposes MySQL externally via COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED +# worldserver-debug-logging.yml -> raises log verbosity via COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED +COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=0 +COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=0 + # ===================== # Project name # ===================== @@ -43,10 +51,10 @@ CONTAINER_POST_INSTALL=ac-post-install # ===================== # Images # ===================== -AC_DB_IMPORT_IMAGE=acore/ac-wotlk-db-import:14.0.0-dev +AC_DB_IMPORT_IMAGE=acore/ac-wotlk-db-import:master # Services (Standard) -AC_AUTHSERVER_IMAGE=acore/ac-wotlk-authserver:14.0.0-dev -AC_WORLDSERVER_IMAGE=acore/ac-wotlk-worldserver:14.0.0-dev +AC_AUTHSERVER_IMAGE=acore/ac-wotlk-authserver:master +AC_WORLDSERVER_IMAGE=acore/ac-wotlk-worldserver:master # Services (Playerbots) AC_AUTHSERVER_IMAGE_PLAYERBOTS=acore-compose:authserver-playerbots AC_WORLDSERVER_IMAGE_PLAYERBOTS=acore-compose:worldserver-playerbots @@ -55,7 +63,7 @@ AC_WORLDSERVER_IMAGE_PLAYERBOTS=acore-compose:worldserver-playerbots 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=acore/ac-wotlk-client-data:master AC_CLIENT_DATA_IMAGE_PLAYERBOTS=uprightbass360/azerothcore-wotlk-playerbots:client-data-Playerbot # Build artifacts DOCKER_IMAGE_TAG=master @@ -101,7 +109,6 @@ MYSQL_ROOT_HOST=% MYSQL_USER=root MYSQL_PORT=3306 MYSQL_EXTERNAL_PORT=64306 -MYSQL_EXPOSE_PORT=0 MYSQL_CHARACTER_SET=utf8mb4 MYSQL_COLLATION=utf8mb4_unicode_ci MYSQL_MAX_CONNECTIONS=1000 diff --git a/README.md b/README.md index 8a1f2a3..b6b035a 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ cd AzerothCore-RealmMaster The setup wizard will guide you through: - **Server Configuration**: IP address, ports, timezone - **Module Selection**: Choose from 30+ available modules or use presets +- **Module Definitions**: Customize defaults in `config/module-manifest.json` and optional presets under `config/module-profiles/` - **Storage Paths**: Configure NFS/local storage locations - **Playerbot Settings**: Max bots, account limits (if enabled) - **Backup Settings**: Retention policies for automated backups @@ -116,7 +117,7 @@ The setup wizard will guide you through: **Required when:** - Playerbots enabled (`MODULE_PLAYERBOTS=1`) -- Any C++ module enabled (modules with `"type": "cpp"` in `config/modules.json`) +- Any C++ module enabled (modules with `"type": "cpp"` in `config/module-manifest.json`) **Build process:** 1. Clones AzerothCore source to `local-storage/source/` @@ -249,7 +250,7 @@ The remote deployment process transfers: - ❌ Build artifacts (source code, compilation files stay local) #### Module Presets -- Define JSON presets in `profiles/*.json`. Each file contains: +- Define JSON presets in `config/module-profiles/*.json`. Each file contains: - `modules` (array, required) – list of `MODULE_*` identifiers to enable. - `label` (string, optional) – text shown in the setup menu (emoji welcome). - `description` (string, optional) – short help text for maintainers. @@ -266,11 +267,12 @@ The remote deployment process transfers: ``` - `setup.sh` automatically adds these presets to the module menu and enables the listed modules when selected or when `--module-config ` is provided. - Built-in presets: - - `profiles/suggested-modules.json` – default solo-friendly QoL stack. - - `profiles/playerbots-suggested-modules.json` – suggested stack plus playerbots. - - `profiles/playerbots-only.json` – playerbot-focused profile (adjust `--playerbot-max-bots`). + - `config/module-profiles/suggested-modules.json` – default solo-friendly QoL stack. + - `config/module-profiles/playerbots-suggested-modules.json` – suggested stack plus playerbots. + - `config/module-profiles/playerbots-only.json` – playerbot-focused profile (adjust `--playerbot-max-bots`). - Custom example: - - `profiles/sam.json` – Sam's playerbot-focused profile (set `--playerbot-max-bots 3000` when using this preset). + - `config/module-profiles/sam.json` – Sam's playerbot-focused profile (set `--playerbot-max-bots 3000` when using this preset). +- Module metadata lives in `config/module-manifest.json`; update that file if you need to add new modules or change repositories/branches. ### Post-Installation Steps @@ -757,7 +759,7 @@ flowchart TB | Service / Container | Role | Ports (host → container) | Profile | |---------------------|------|--------------------------|---------| -| `ac-mysql` | MySQL 8.0 database | *(optional)* `64306 → 3306` (`MYSQL_EXPOSE_PORT=1`) | `db` | +| `ac-mysql` | MySQL 8.0 database | *(optional)* `64306 → 3306` (`COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=1`) | `db` | | `ac-db-init` | Database schema initialization | – | `db` | | `ac-db-import` | Database content import | – | `db` | | `ac-backup` | Automated backup system | – | `db` | @@ -775,10 +777,22 @@ flowchart TB ### Database Hardening -- **MySQL port exposure** – By default `MYSQL_EXPOSE_PORT=0`, so `ac-mysql` is reachable only from the internal Docker network. Set `MYSQL_EXPOSE_PORT=1` to publish `${MYSQL_EXTERNAL_PORT}` on the host; RealmMaster scripts automatically include `docker-compose.mysql-expose.yml` so the override Just Works. If you invoke Compose manually, remember to add `-f docker-compose.mysql-expose.yml`. +- **MySQL port exposure** – By default `COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=0`, so `ac-mysql` is reachable only from the internal Docker network. Set it to `1` to publish `${MYSQL_EXTERNAL_PORT}` on the host; RealmMaster scripts automatically include `compose-overrides/mysql-expose.yml` so the override Just Works. If you invoke Compose manually, remember to add `-f compose-overrides/mysql-expose.yml`. You can follow the same `COMPOSE_OVERRIDE__ENABLED=1` pattern for any custom override files you drop into `compose-overrides/`. +- **Worldserver debug logging** – Need extra verbosity temporarily? Flip `COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=1` to include `compose-overrides/worldserver-debug-logging.yml`, which bumps `AC_LOG_LEVEL` across all worldserver profiles. Turn it back off once you’re done to avoid noisy logs. - **Binary logging toggle** – `MYSQL_DISABLE_BINLOG=1` appends `--skip-log-bin` via the MySQL wrapper entrypoint to keep disk churn low (and match Playerbot guidance). Flip the flag to `0` to re-enable binlogs for debugging or replication. - **Drop-in configs** – Any `.cnf` placed in `${STORAGE_PATH}/config/mysql/conf.d` (exposed via `MYSQL_CONFIG_DIR`) is mounted into `/etc/mysql/conf.d`. Use this to add custom tunables or temporarily override the binlog setting without touching the image. +### Compose Overrides + +All helper scripts automatically include any override file found in `compose-overrides/` when its matching flag `COMPOSE_OVERRIDE__ENABLED` is set to `1` in `.env`. Each override declares its flag at the top with `# override-flag: ...`. This lets you ship opt-in tweaks without editing `docker-compose.yml`. + +Current examples: + +- `compose-overrides/mysql-expose.yml` (`COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED`) – Publishes MySQL to `${MYSQL_EXTERNAL_PORT}` for external clients. +- `compose-overrides/worldserver-debug-logging.yml` (`COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED`) – Raises `AC_LOG_LEVEL` to `3` across all worldserver profiles for troubleshooting. + +Add your own override by dropping a new `.yml` file into `compose-overrides/`, documenting the flag name in a comment, and toggling that flag in `.env`. + ### Storage Structure The project uses a dual-storage approach for optimal performance: @@ -986,9 +1000,9 @@ Internal script that runs inside the `ac-modules` container to handle module lif - Manages module configuration files - Tracks installation state -#### `config/modules.json` & `scripts/modules.py` +#### `config/module-manifest.json` & `scripts/modules.py` Central module registry and management system: -- **`config/modules.json`** - Declarative manifest defining all 30+ supported modules with metadata: +- **`config/module-manifest.json`** - Declarative manifest defining all 30+ supported modules with metadata: - Repository URLs - Module type (cpp, data, lua) - Build requirements diff --git a/build.sh b/build.sh index 3a4c90f..38fc5c0 100755 --- a/build.sh +++ b/build.sh @@ -114,7 +114,7 @@ generate_module_state(){ 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 + if ! python3 "$MODULE_HELPER" --env-path "$ENV_PATH" --manifest "$ROOT_DIR/config/module-manifest.json" generate --output-dir "$output_dir"; then err "Module manifest validation failed. See errors above." exit 1 fi diff --git a/cleanup.sh b/cleanup.sh index fda2d05..94548ba 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -13,6 +13,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="${SCRIPT_DIR}" DEFAULT_COMPOSE_FILE="${PROJECT_DIR}/docker-compose.yml" ENV_FILE="${PROJECT_DIR}/.env" +source "${PROJECT_DIR}/scripts/lib/compose_overrides.sh" +declare -a COMPOSE_FILE_ARGS=() # Colors RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; MAGENTA='\033[0;35m'; NC='\033[0m' @@ -125,15 +127,7 @@ if [ -f "$ENV_FILE" ]; then set -a; source "$ENV_FILE"; set +a fi -COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") -if [ "${MYSQL_EXPOSE_PORT:-0}" = "1" ]; then - EXTRA_COMPOSE_FILE="${PROJECT_DIR}/docker-compose.mysql-expose.yml" - if [ -f "$EXTRA_COMPOSE_FILE" ]; then - COMPOSE_FILE_ARGS+=(-f "$EXTRA_COMPOSE_FILE") - else - print_status WARNING "MYSQL_EXPOSE_PORT=1 but $EXTRA_COMPOSE_FILE missing; skipping port exposure override." - fi -fi +compose_overrides::build_compose_args "$PROJECT_DIR" "$ENV_FILE" "$DEFAULT_COMPOSE_FILE" COMPOSE_FILE_ARGS COMPOSE_FILE_ARGS_STR="" for arg in "${COMPOSE_FILE_ARGS[@]}"; do COMPOSE_FILE_ARGS_STR+=" ${arg}" diff --git a/compose-overrides/mysql-expose.yml b/compose-overrides/mysql-expose.yml new file mode 100644 index 0000000..38a095e --- /dev/null +++ b/compose-overrides/mysql-expose.yml @@ -0,0 +1,12 @@ +# override-flag: COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED +# legacy-flag: MYSQL_EXPOSE_PORT +# Optional override file that publishes MySQL to the host. Use it only when you +# intentionally need direct access (e.g., external DB clients). RealmMaster scripts +# auto-include this file whenever `COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=1` +# (or the legacy `MYSQL_EXPOSE_PORT=1`) in `.env`. When running Compose manually, add: +# docker compose -f docker-compose.yml -f compose-overrides/mysql-expose.yml up -d +# Reset the flag to 0 to return to the secure, internal-only default. +services: + ac-mysql: + ports: + - "${MYSQL_EXTERNAL_PORT}:${MYSQL_PORT}" diff --git a/compose-overrides/worldserver-debug-logging.yml b/compose-overrides/worldserver-debug-logging.yml new file mode 100644 index 0000000..a3e335c --- /dev/null +++ b/compose-overrides/worldserver-debug-logging.yml @@ -0,0 +1,15 @@ +# override-flag: COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED +# Example override that bumps worldserver log verbosity across all profiles. +# Enable by setting COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=1 in .env. +# Helpful while investigating live issues; remember to turn it off to reduce noise. +# Follow this pattern for any compose layers you want to implement +services: + ac-worldserver-standard: + environment: + AC_LOG_LEVEL: "3" + ac-worldserver-playerbots: + environment: + AC_LOG_LEVEL: "3" + ac-worldserver-modules: + environment: + AC_LOG_LEVEL: "3" diff --git a/config/modules.json b/config/module-manifest.json similarity index 100% rename from config/modules.json rename to config/module-manifest.json diff --git a/profiles/sam.json b/config/module-profiles/RealmMaster.json similarity index 68% rename from profiles/sam.json rename to config/module-profiles/RealmMaster.json index 59fb957..74fcdf9 100644 --- a/profiles/sam.json +++ b/config/module-profiles/RealmMaster.json @@ -1,28 +1,39 @@ { "modules": [ - "MODULE_ELUNA", "MODULE_PLAYERBOTS", - "MODULE_PLAYER_BOT_LEVEL_BRACKETS", - "MODULE_SOLO_LFG", - "MODULE_TRANSMOG", - "MODULE_NPC_BUFFER", "MODULE_LEARN_SPELLS", "MODULE_FIREWORKS", - "MODULE_REAGENT_BANK", - "MODULE_BLACK_MARKET_AUCTION_HOUSE", + "MODULE_TRANSMOG", + "MODULE_NPC_BUFFER", + "MODULE_SOLO_LFG", "MODULE_1V1_ARENA", - "MODULE_ACCOUNT_ACHIEVEMENTS", "MODULE_BREAKING_NEWS", "MODULE_BOSS_ANNOUNCER", + "MODULE_ACCOUNT_ACHIEVEMENTS", "MODULE_AUTO_REVIVE", - "MODULE_ELUNA_TS", + "MODULE_GAIN_HONOR_GUARD", + "MODULE_ELUNA", + "MODULE_TIME_IS_TIME", + "MODULE_RANDOM_ENCHANTS", + "MODULE_SOLOCRAFT", "MODULE_NPC_BEASTMASTER", "MODULE_NPC_ENCHANTER", - "MODULE_RANDOM_ENCHANTS", "MODULE_INSTANCE_RESET", - "MODULE_TIME_IS_TIME", - "MODULE_GAIN_HONOR_GUARD", - "MODULE_ARAC" + "MODULE_ARAC", + "MODULE_ASSISTANT", + "MODULE_REAGENT_BANK", + "MODULE_BLACK_MARKET_AUCTION_HOUSE", + "MODULE_STATBOOSTER", + "MODULE_ELUNA_TS", + "MODULE_AIO", + "MODULE_ELUNA_SCRIPTS", + "MODULE_EVENT_SCRIPTS", + "MODULE_ACTIVE_CHAT", + "MODULE_GUILDHOUSE", + "MODULE_NPC_FREE_PROFESSIONS", + "MODULE_MORPHSUMMON", + "MODULE_ITEM_LEVEL_UP", + "MODULE_GLOBAL_CHAT" ], "label": "\ud83e\udde9 Sam", "description": "Sam's playerbot-centric preset (use high bot counts)", diff --git a/profiles/all-modules.json b/config/module-profiles/all-modules.json similarity index 100% rename from profiles/all-modules.json rename to config/module-profiles/all-modules.json diff --git a/profiles/playerbots-only.json b/config/module-profiles/playerbots-only.json similarity index 100% rename from profiles/playerbots-only.json rename to config/module-profiles/playerbots-only.json diff --git a/profiles/playerbots-suggested-modules.json b/config/module-profiles/playerbots-suggested-modules.json similarity index 100% rename from profiles/playerbots-suggested-modules.json rename to config/module-profiles/playerbots-suggested-modules.json diff --git a/profiles/suggested-modules.json b/config/module-profiles/suggested-modules.json similarity index 100% rename from profiles/suggested-modules.json rename to config/module-profiles/suggested-modules.json diff --git a/deploy.sh b/deploy.sh index 02459a7..9056a01 100755 --- a/deploy.sh +++ b/deploy.sh @@ -11,6 +11,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEFAULT_COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" ENV_PATH="$ROOT_DIR/.env" +source "$ROOT_DIR/scripts/lib/compose_overrides.sh" TARGET_PROFILE="" WATCH_LOGS=1 KEEP_RUNNING=0 @@ -280,17 +281,7 @@ read_env(){ } init_compose_files(){ - local expose_port - expose_port="$(read_env MYSQL_EXPOSE_PORT "0")" - COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") - if [ "$expose_port" = "1" ]; then - local extra_file="$ROOT_DIR/docker-compose.mysql-expose.yml" - if [ -f "$extra_file" ]; then - COMPOSE_FILE_ARGS+=(-f "$extra_file") - else - warn "MYSQL_EXPOSE_PORT=1 but $extra_file not found; skipping port override" - fi - fi + compose_overrides::build_compose_args "$ROOT_DIR" "$ENV_PATH" "$DEFAULT_COMPOSE_FILE" COMPOSE_FILE_ARGS } init_compose_files @@ -337,7 +328,7 @@ ensure_module_state(){ 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 + if ! python3 "$MODULE_HELPER" --env-path "$ENV_PATH" --manifest "$ROOT_DIR/config/module-manifest.json" generate --output-dir "$output_dir"; then err "Module manifest validation failed. See errors above." fi diff --git a/docker-compose.mysql-expose.yml b/docker-compose.mysql-expose.yml deleted file mode 100644 index 467832b..0000000 --- a/docker-compose.mysql-expose.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - ac-mysql: - # Optional override that publishes the MySQL port when MYSQL_EXPOSE_PORT=1 - ports: - - "${MYSQL_EXTERNAL_PORT}:${MYSQL_PORT}" diff --git a/docker-compose.yml b/docker-compose.yml index 5cabb68..6fb943d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -596,7 +596,7 @@ services: env_file: - ./.env environment: - MODULES_MANIFEST_PATH: /tmp/config/modules.json + MODULES_MANIFEST_PATH: /tmp/config/module-manifest.json entrypoint: ["/bin/sh"] command: - -c diff --git a/docs/installing-azerothcore-with-docker.md b/docs/installing-azerothcore-with-docker.md index 96c0bf1..1a5fe51 100644 --- a/docs/installing-azerothcore-with-docker.md +++ b/docs/installing-azerothcore-with-docker.md @@ -14,9 +14,27 @@ This guide mirrors the community “Installing AzerothCore using Docker” workf RealmMaster keeps the familiar `docker-compose.yml` at the repo root. Instead of editing the YAML directly, run `./setup.sh` (see [README → Getting Started](../README.md#getting-started)) to generate `.env`; every setting from storage paths and ports to module toggles lives there. This mirrors the upstream “use docker-compose.override.yml” advice while preserving a single declarative stack. -- **Security**: databases stay on the internal `azerothcore` bridge and never publish MySQL ports unless you explicitly set `MYSQL_EXPOSE_PORT` in `.env`. Binary logging is disabled via `MYSQL_DISABLE_BINLOG=1`, matching the upstream recommendation for playerbots. +- **Security**: databases stay on the internal `azerothcore` bridge and never publish MySQL ports unless you explicitly set `COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=1` in `.env`. Binary logging is disabled via `MYSQL_DISABLE_BINLOG=1`, matching the upstream recommendation for playerbots. - **Storage**: bind mounts map to `storage/` and `local-storage/`, ensuring data survives container rebuilds just like the original bind-mount instructions. `ac-volume-init` and `ac-storage-init` bootstrap ownership so you do not need to chown paths manually. - **Networks & profiles**: all services share the `azerothcore` bridge, and Compose profiles (`services-standard`, `services-playerbots`, `services-modules`, `tools`) let you enable only what you need, similar to copying multiple override files upstream. +- **Override toggles**: drop-in files under `compose-overrides/` (like `mysql-expose.yml` for port exposure or `worldserver-debug-logging.yml` for verbose logs) can be activated by setting `COMPOSE_OVERRIDE__ENABLED=1` in `.env`, so you can extend the stack without editing the main compose file. +- **Module manifest**: all module metadata lives in `config/module-manifest.json`; presets surfaced in `setup.sh` come from `config/module-profiles/*.json`, so you can adapt the same workflow the upstream document used by editing those files. + +### Override Examples + +RealmMaster ships with two opt-in overrides to demonstrate the pattern: + +- `compose-overrides/mysql-expose.yml` (`COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=1`) publishes MySQL on `${MYSQL_EXTERNAL_PORT}` for IDEs or external tooling. +- `compose-overrides/worldserver-debug-logging.yml` (`COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=1`) bumps `AC_LOG_LEVEL` to `3` across every worldserver profile for troubleshooting. + +Add your own overrides by dropping a `.yml` file into `compose-overrides/` with a `# override-flag: ...` header and toggling the matching env flag. All project scripts automatically include enabled overrides, so the workflow mirrors the upstream “override file” approach without manual compose arguments. + +### Module Layout + +- **Manifest**: `config/module-manifest.json` tracks every supported module (type, repo, dependencies). Edit this if you need to add or update modules—`scripts/modules.py` and all container helpers consume it automatically. +- **Presets**: `config/module-profiles/*.json` replaces the old `profiles/*.json`. Each preset defines a `modules` list plus optional `label/description/order`, and `setup.sh` surfaces them in the module-selection menu or via `--module-config `. + +Because the manifest/preset locations mirror the upstream structure conceptually, experienced users can jump straight into editing those files without re-learning the workflow. Example excerpt (trimmed for clarity): diff --git a/scripts/check_module_staging.py b/scripts/check_module_staging.py index 576d55d..2d70841 100755 --- a/scripts/check_module_staging.py +++ b/scripts/check_module_staging.py @@ -8,7 +8,7 @@ from pathlib import Path def load_module_state(root: Path) -> dict: env_path = root / ".env" - manifest_path = root / "config" / "modules.json" + manifest_path = root / "config" / "module-manifest.json" modules_py = root / "scripts" / "modules.py" try: diff --git a/scripts/deploy-tools.sh b/scripts/deploy-tools.sh index dd73031..f639a9d 100755 --- a/scripts/deploy-tools.sh +++ b/scripts/deploy-tools.sh @@ -7,6 +7,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.." DEFAULT_COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" ENV_FILE="$ROOT_DIR/.env" +source "$ROOT_DIR/scripts/lib/compose_overrides.sh" declare -a COMPOSE_FILE_ARGS=() BLUE='\033[0;34m' @@ -46,15 +47,7 @@ resolve_project_name(){ } init_compose_files(){ - COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") - if [ "$(read_env MYSQL_EXPOSE_PORT "0")" = "1" ]; then - local extra_file="$ROOT_DIR/docker-compose.mysql-expose.yml" - if [ -f "$extra_file" ]; then - COMPOSE_FILE_ARGS+=(-f "$extra_file") - else - warn "MYSQL_EXPOSE_PORT=1 but $extra_file missing; skipping port override." - fi - fi + compose_overrides::build_compose_args "$ROOT_DIR" "$ENV_FILE" "$DEFAULT_COMPOSE_FILE" COMPOSE_FILE_ARGS } init_compose_files diff --git a/scripts/hooks/REFACTORING_SUMMARY.md b/scripts/hooks/REFACTORING_SUMMARY.md deleted file mode 100644 index f2bbbd6..0000000 --- a/scripts/hooks/REFACTORING_SUMMARY.md +++ /dev/null @@ -1,49 +0,0 @@ -# Post-Install Hooks Refactoring Summary - -## What Was Accomplished - -### 1. **Legacy System Issues** -- Hardcoded hooks in `manage-modules.sh` (only 2 implemented) -- 26 undefined hooks from Eluna modules causing warnings -- No extensibility or maintainability - -### 2. **New Architecture Implemented** - -#### **External Hook Scripts** (`scripts/hooks/`) -- `copy-standard-lua` - Generic Lua script copying for Eluna modules -- `copy-aio-lua` - AIO-specific Lua script handling -- `mod-ale-patches` - mod-ale compatibility patches -- `black-market-setup` - Black Market specific setup -- `README.md` - Complete documentation - -#### **Manifest-Driven Configuration** -- All hooks now defined in `config/modules.json` -- Standardized hook names (kebab-case) -- No more undefined hooks - -#### **Refactored Hook Runner** (`manage-modules.sh`) -- External script execution with environment variables -- Proper error handling (exit codes 0/1/2) -- Environment cleanup -- Removed legacy fallback code - -### 3. **Hook Mapping Applied** -- **24 Eluna modules** → `copy-standard-lua` -- **2 AIO modules** → `copy-aio-lua` -- **1 mod-ale module** → `mod-ale-patches` -- **1 Black Market module** → `black-market-setup` - -### 4. **Benefits Achieved** -- ✅ **Maintainable** - Hooks are separate, reusable scripts -- ✅ **Extensible** - Easy to add new hooks without code changes -- ✅ **Reliable** - No more undefined hook warnings -- ✅ **Documented** - Clear interface and usage patterns -- ✅ **Clean** - Removed legacy code and hardcoded cases - -## Files Modified -- `scripts/hooks/` (new directory with 5 files) -- `scripts/manage-modules.sh` (refactored hook runner) -- `config/modules.json` (updated all 28 hook definitions) - -## Testing Ready -The system is ready for testing with the modules container to ensure all Lua scripts are properly copied to `/azerothcore/lua_scripts` during module installation. \ No newline at end of file diff --git a/scripts/lib/compose_overrides.sh b/scripts/lib/compose_overrides.sh new file mode 100644 index 0000000..8668c3b --- /dev/null +++ b/scripts/lib/compose_overrides.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +# Helper utilities for dynamically including docker compose override files +# based on FEATURE_NAME_ENABLED style environment flags. + +compose_overrides::trim() { + local value="$1" + # shellcheck disable=SC2001 + value="$(echo "$value" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + printf '%s' "$value" +} + +compose_overrides::derive_flag_from_name() { + local file="$1" + local base + base="$(basename "$file")" + base="${base%.*}" + base="${base//[^[:alnum:]]/_}" + base="${base^^}" + printf 'COMPOSE_OVERRIDE_%s_ENABLED' "$base" +} + +compose_overrides::extract_tag() { + local file="$1" tag="$2" + local line + line="$(grep -m1 "^# *${tag}:" "$file" 2>/dev/null || true)" + if [ -z "$line" ]; then + return 1 + fi + line="${line#*:}" + compose_overrides::trim "$line" +} + +compose_overrides::extract_all_tags() { + local file="$1" tag="$2" + grep "^# *${tag}:" "$file" 2>/dev/null | cut -d':' -f2- | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' +} + +compose_overrides::read_env_value() { + local env_path="$1" key="$2" default="${3:-}" + local value="" + if [ -f "$env_path" ]; then + value="$(grep -E "^${key}=" "$env_path" | tail -n1 | cut -d'=' -f2- | tr -d '\r')" + fi + if [ -z "$value" ]; then + value="$default" + fi + printf '%s' "$value" +} + +compose_overrides::list_enabled_files() { + local root_dir="$1" env_path="$2" result_var="$3" + local overrides_dir="${root_dir}/compose-overrides" + local -n __result="$result_var" + __result=() + + [ -d "$overrides_dir" ] || return 0 + + local -a override_files=() + while IFS= read -r -d '' file; do + override_files+=("$file") + done < <(find "$overrides_dir" -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print0 | sort -z) + + local file flag flag_value legacy_default legacy_flags legacy_flag + for file in "${override_files[@]}"; do + flag="$(compose_overrides::extract_tag "$file" "override-flag" || true)" + if [ -z "$flag" ]; then + flag="$(compose_overrides::derive_flag_from_name "$file")" + fi + + legacy_default="0" + legacy_flags="$(compose_overrides::extract_all_tags "$file" "legacy-flag" || true)" + if [ -n "$legacy_flags" ]; then + while IFS= read -r legacy_flag; do + [ -z "$legacy_flag" ] && continue + legacy_default="$(compose_overrides::read_env_value "$env_path" "$legacy_flag" "$legacy_default")" + # Stop at first legacy flag that yields a value + if [ -n "$legacy_default" ]; then + break + fi + done <<< "$legacy_flags" + fi + + flag_value="$(compose_overrides::read_env_value "$env_path" "$flag" "$legacy_default")" + if [ "$flag_value" = "1" ]; then + __result+=("$file") + fi + done +} + +compose_overrides::build_compose_args() { + local root_dir="$1" env_path="$2" default_compose="$3" result_var="$4" + local -n __result="$result_var" + __result=(-f "$default_compose") + + local -a enabled_files=() + compose_overrides::list_enabled_files "$root_dir" "$env_path" enabled_files + for file in "${enabled_files[@]}"; do + __result+=(-f "$file") + done +} diff --git a/scripts/manage-modules-sql.sh b/scripts/manage-modules-sql.sh index 714117e..ccb3a6b 100755 --- a/scripts/manage-modules-sql.sh +++ b/scripts/manage-modules-sql.sh @@ -130,7 +130,7 @@ ensure_module_metadata(){ fi done - local manifest_path="${MANIFEST_PATH:-${MODULES_MANIFEST_PATH:-/tmp/config/modules.json}}" + local manifest_path="${MANIFEST_PATH:-${MODULES_MANIFEST_PATH:-/tmp/config/module-manifest.json}}" local env_path="${ENV_PATH:-${MODULES_ENV_PATH:-/tmp/.env}}" local state_env_candidate="${STATE_DIR:-${MODULES_ROOT:-/modules}}/modules.env" if [ -f "$state_env_candidate" ]; then diff --git a/scripts/manage-modules.sh b/scripts/manage-modules.sh index c13ea88..952c55d 100755 --- a/scripts/manage-modules.sh +++ b/scripts/manage-modules.sh @@ -55,22 +55,22 @@ resolve_manifest_path(){ return fi local candidate - candidate="$PROJECT_ROOT/config/modules.json" + candidate="$PROJECT_ROOT/config/module-manifest.json" if [ -f "$candidate" ]; then echo "$candidate" return fi - candidate="$SCRIPT_DIR/../config/modules.json" + candidate="$SCRIPT_DIR/../config/module-manifest.json" if [ -f "$candidate" ]; then echo "$candidate" return fi - candidate="/tmp/config/modules.json" + candidate="/tmp/config/module-manifest.json" if [ -f "$candidate" ]; then echo "$candidate" return fi - err "Unable to locate module manifest (set MODULES_MANIFEST_PATH or ensure config/modules.json exists)" + err "Unable to locate module manifest (set MODULES_MANIFEST_PATH or ensure config/module-manifest.json exists)" } setup_git_config(){ diff --git a/scripts/modules.py b/scripts/modules.py index 9fec09c..dab30e7 100755 --- a/scripts/modules.py +++ b/scripts/modules.py @@ -2,7 +2,7 @@ """ Module manifest helper. -Reads config/modules.json and .env to produce canonical module state that +Reads config/module-manifest.json and .env to produce canonical module state that downstream shell scripts can consume for staging, rebuild detection, and dependency validation. """ @@ -466,8 +466,8 @@ def configure_parser() -> argparse.ArgumentParser: ) parser.add_argument( "--manifest", - default="config/modules.json", - help="Path to module manifest (default: config/modules.json)", + default="config/module-manifest.json", + help="Path to module manifest (default: config/module-manifest.json)", ) subparsers = parser.add_subparsers(dest="command", required=True) diff --git a/scripts/rebuild-with-modules.sh b/scripts/rebuild-with-modules.sh index d9fe541..1f6521f 100755 --- a/scripts/rebuild-with-modules.sh +++ b/scripts/rebuild-with-modules.sh @@ -179,7 +179,7 @@ ensure_module_state(){ local storage_root storage_root="$(resolve_local_storage_path)" MODULE_STATE_DIR="${storage_root}/modules" - if ! python3 "$MODULE_HELPER" --env-path "$ENV_FILE" --manifest "$PROJECT_DIR/config/modules.json" generate --output-dir "$MODULE_STATE_DIR"; then + if ! python3 "$MODULE_HELPER" --env-path "$ENV_FILE" --manifest "$PROJECT_DIR/config/module-manifest.json" generate --output-dir "$MODULE_STATE_DIR"; then echo "❌ Module manifest validation failed. See details above." exit 1 fi @@ -463,7 +463,7 @@ remove_sentinel(){ fi if command -v docker >/dev/null 2>&1; then local db_image - db_image="$(read_env AC_DB_IMPORT_IMAGE "acore/ac-wotlk-db-import:14.0.0-dev")" + db_image="$(read_env AC_DB_IMPORT_IMAGE "acore/ac-wotlk-db-import:master")" if docker image inspect "$db_image" >/dev/null 2>&1; then local mount_dir mount_dir="$(dirname "$sentinel_path")" diff --git a/scripts/stage-modules.sh b/scripts/stage-modules.sh index e2870e1..84b51af 100755 --- a/scripts/stage-modules.sh +++ b/scripts/stage-modules.sh @@ -65,7 +65,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" ENV_FILE="$PROJECT_DIR/.env" DEFAULT_COMPOSE_FILE="$PROJECT_DIR/docker-compose.yml" -EXTRA_COMPOSE_FILE="$PROJECT_DIR/docker-compose.mysql-expose.yml" +source "$PROJECT_DIR/scripts/lib/compose_overrides.sh" usage(){ cat <.json + --module-config NAME Use preset NAME from config/module-profiles/.json --enable-modules LIST Comma-separated module list (MODULE_* or shorthand) --playerbot-enabled 0|1 Override PLAYERBOT_ENABLED flag --playerbot-min-bots N Override PLAYERBOT_MIN_BOTS value @@ -935,7 +936,7 @@ fi declare -A MODULE_PRESET_LABELS=() declare -A MODULE_PRESET_DESCRIPTIONS=() declare -A MODULE_PRESET_ORDER=() - local CONFIG_DIR="$SCRIPT_DIR/profiles" + local CONFIG_DIR="$SCRIPT_DIR/config/module-profiles" if [ ! -x "$MODULE_PROFILES_HELPER" ]; then say ERROR "Profile helper not found or not executable at $MODULE_PROFILES_HELPER" exit 1 @@ -953,7 +954,7 @@ fi local missing_presets=0 for required_preset in "$DEFAULT_PRESET_SUGGESTED" "$DEFAULT_PRESET_PLAYERBOTS"; do if [ -z "${MODULE_PRESET_CONFIGS[$required_preset]:-}" ]; then - say ERROR "Missing module preset profiles/${required_preset}.json" + say ERROR "Missing module preset config/module-profiles/${required_preset}.json" missing_presets=1 fi done @@ -1036,7 +1037,7 @@ fi else pretty_name=$(echo "$preset_name" | tr '_-' ' ' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1)) substr($i,2)}}1') fi - echo "${menu_index}) ${pretty_name} (profiles/${preset_name}.json)" + echo "${menu_index}) ${pretty_name} (config/module-profiles/${preset_name}.json)" MENU_PRESET_INDEX[$menu_index]="$preset_name" menu_index=$((menu_index + 1)) done @@ -1454,7 +1455,8 @@ fi HOST_ZONEINFO_PATH=${HOST_ZONEINFO_PATH:-$DEFAULT_HOST_ZONEINFO_PATH} MYSQL_INNODB_REDO_LOG_CAPACITY=${MYSQL_INNODB_REDO_LOG_CAPACITY:-$DEFAULT_MYSQL_INNODB_REDO_LOG_CAPACITY} MYSQL_RUNTIME_TMPFS_SIZE=${MYSQL_RUNTIME_TMPFS_SIZE:-$DEFAULT_MYSQL_RUNTIME_TMPFS_SIZE} - MYSQL_EXPOSE_PORT=${MYSQL_EXPOSE_PORT:-$DEFAULT_MYSQL_EXPOSE_PORT} + COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=${COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED:-$DEFAULT_COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED} + COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=${COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED:-$DEFAULT_COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED} MYSQL_DISABLE_BINLOG=${MYSQL_DISABLE_BINLOG:-$DEFAULT_MYSQL_DISABLE_BINLOG} MYSQL_CONFIG_DIR=${MYSQL_CONFIG_DIR:-$DEFAULT_MYSQL_CONFIG_DIR} CLIENT_DATA_PATH=${CLIENT_DATA_PATH:-$DEFAULT_CLIENT_DATA_PATH} @@ -1502,6 +1504,12 @@ fi cat < exposes MySQL externally via COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED +# worldserver-debug-logging.yml -> raises log verbosity via COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED +COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED=$COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED +COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED=$COMPOSE_OVERRIDE_WORLDSERVER_DEBUG_LOGGING_ENABLED + COMPOSE_PROJECT_NAME=$DEFAULT_COMPOSE_PROJECT_NAME STORAGE_PATH=$STORAGE_PATH @@ -1514,9 +1522,8 @@ MYSQL_IMAGE=$DEFAULT_MYSQL_IMAGE MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD MYSQL_ROOT_HOST=$DEFAULT_MYSQL_ROOT_HOST MYSQL_USER=$DEFAULT_MYSQL_USER -MYSQL_PORT=$DEFAULT_MYSQL_INTERNAL_PORT -MYSQL_EXTERNAL_PORT=$MYSQL_EXTERNAL_PORT -MYSQL_EXPOSE_PORT=${MYSQL_EXPOSE_PORT:-$DEFAULT_MYSQL_EXPOSE_PORT} + MYSQL_PORT=$DEFAULT_MYSQL_INTERNAL_PORT + MYSQL_EXTERNAL_PORT=$MYSQL_EXTERNAL_PORT MYSQL_DISABLE_BINLOG=${MYSQL_DISABLE_BINLOG:-$DEFAULT_MYSQL_DISABLE_BINLOG} MYSQL_CONFIG_DIR=${MYSQL_CONFIG_DIR:-$DEFAULT_MYSQL_CONFIG_DIR} MYSQL_CHARACTER_SET=$DEFAULT_MYSQL_CHARACTER_SET diff --git a/status.sh b/status.sh index 2dedd1f..307e456 100755 --- a/status.sh +++ b/status.sh @@ -53,7 +53,7 @@ AUTH_PORT="$(read_env AUTH_EXTERNAL_PORT)" WORLD_PORT="$(read_env WORLD_EXTERNAL_PORT)" SOAP_PORT="$(read_env SOAP_EXTERNAL_PORT)" MYSQL_PORT="$(read_env MYSQL_EXTERNAL_PORT)" -MYSQL_EXPOSE_PORT="$(read_env MYSQL_EXPOSE_PORT)" +MYSQL_EXPOSE_OVERRIDE="$(read_env COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED "$(read_env MYSQL_EXPOSE_PORT "0")")" PMA_PORT="$(read_env PMA_EXTERNAL_PORT)" KEIRA_PORT="$(read_env KEIRA3_EXTERNAL_PORT)" ELUNA_ENABLED="$(read_env AC_ELUNA_ENABLED)" @@ -255,7 +255,7 @@ ports_summary(){ for i in "${!names[@]}"; do local svc="${names[$i]}" local port="${ports[$i]}" - if [ "$svc" = "MySQL" ] && [ "${MYSQL_EXPOSE_PORT}" != "1" ]; then + if [ "$svc" = "MySQL" ] && [ "${MYSQL_EXPOSE_OVERRIDE}" != "1" ]; then printf " %-10s %-6s %b○%b not exposed\n" "$svc" "--" "$CYAN" "$NC" continue fi