name: WoW integration lab on: push: branches: - feature/ai-brain-router paths: - "ale/**" - "core/**" - "scripts/ale-capture-server.mjs" - ".github/workflows/wow-integration.yml" workflow_dispatch: jobs: real-worldserver: runs-on: ubuntu-latest timeout-minutes: 60 env: DOCKER_VOL_DATA: /tmp/ac-client-data DOCKER_VOL_ETC: /tmp/ac-etc DOCKER_VOL_LOGS: /tmp/ac-logs DOCKER_DB_ROOT_PASSWORD: ci-password ALE_BRIDGE_TOKEN: ci-bridge-token steps: - name: Checkout AzerothAI uses: actions/checkout@v4 - name: Start disposable AzerothAI bridge receiver run: | nohup node scripts/ale-capture-server.mjs >/tmp/ale-capture-server.log 2>&1 & for i in {1..30}; do if curl -fsS http://127.0.0.1:3000/health >/dev/null; then exit 0; fi sleep 1 done cat /tmp/ale-capture-server.log exit 1 - name: Clone Playerbots AzerothCore test server run: | git clone --depth 1 --branch Playerbot https://github.com/mod-playerbots/azerothcore-wotlk.git "/tmp/acore" git clone --depth 1 --branch master https://github.com/mod-playerbots/mod-playerbots.git "/tmp/acore/modules/mod-playerbots" git clone --depth 1 https://github.com/azerothcore/mod-ale.git "/tmp/acore/modules/mod-ale" - name: Install AzerothAI ALE bridge into test worldserver run: | mkdir -p "/tmp/acore/ci-lua" cp ale/azerothai_bridge.lua "/tmp/acore/ci-lua/azerothai_bridge.lua" python3 - <<'PY' from pathlib import Path p=Path("/tmp/acore/ci-lua/azerothai_bridge.lua") s=p.read_text() s=s.replace('local ROOT = GetConfigValue("AzerothAI.Url")','local ROOT = "http://host.docker.internal:3000"') s=s.replace('local TOKEN = GetConfigValue("AzerothAI.Token")','local TOKEN = "ci-bridge-token"') s=s.replace('local POLL_MS = tonumber(GetConfigValue("AzerothAI.PollMs")) or 750','local POLL_MS = 250') s=s.replace('local PROTECTED = GetConfigValue("AzerothAI.ProtectedCharacter")','local PROTECTED = "Zaku"') p.write_text(s) PY cat > "/tmp/acore/docker-compose.override.yml" <<'YAML' services: ac-worldserver: extra_hosts: - "host.docker.internal:host-gateway" environment: AC_ALE_ENABLED: "1" AC_ALE_TRACEBACK: "1" AC_ALE_SCRIPTPATH: "lua_scripts" AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0" AC_AI_PLAYERBOT_MIN_RANDOM_BOTS: "0" AC_AI_PLAYERBOT_MAX_RANDOM_BOTS: "0" volumes: - ./modules:/azerothcore/modules:ro - ./ci-lua:/azerothcore/env/dist/bin/lua_scripts:ro YAML - name: Prepare writable test volumes run: | mkdir -p "${DOCKER_VOL_DATA}" "${DOCKER_VOL_ETC}" "${DOCKER_VOL_LOGS}" chmod 777 "${DOCKER_VOL_DATA}" "${DOCKER_VOL_ETC}" "${DOCKER_VOL_LOGS}" - name: Build real AzerothCore + Playerbots + ALE working-directory: /tmp/acore run: | docker compose build ac-client-data-init ac-db-import ac-authserver ac-worldserver - name: Start MySQL working-directory: /tmp/acore run: | docker compose up -d ac-database for i in {1..60}; do if docker inspect --format='{{json .State.Health.Status}}' ac-database 2>/dev/null | grep -q healthy; then exit 0; fi sleep 2 done docker compose logs ac-database exit 1 - name: Load AzerothCore client data working-directory: /tmp/acore run: | set +e docker compose up --no-deps ac-client-data-init rc=$? set -e if [ "$rc" -ne 0 ]; then echo "Official client-data initializer failed; applying CI fallback." rm -rf "${DOCKER_VOL_DATA:?}"/* asset=$(curl -fsSL https://api.github.com/repos/wowgaming/client-data/releases/latest | jq -r '.assets[] | select(.name=="Data.zip" or .name=="data.zip") | .browser_download_url' | head -1) test -n "$asset" curl -fL --retry 3 "$asset" -o /tmp/ac-client-data.zip unzip -q /tmp/ac-client-data.zip -d "${DOCKER_VOL_DATA}" fi test -d "${DOCKER_VOL_DATA}/dbc" test -d "${DOCKER_VOL_DATA}/maps" - name: Import AzerothCore databases working-directory: /tmp/acore run: | docker compose up --no-deps --abort-on-container-exit --exit-code-from ac-db-import ac-db-import - name: Boot authserver and worldserver working-directory: /tmp/acore run: | docker compose up -d --no-deps ac-authserver docker compose up -d --no-deps ac-worldserver for i in {1..180}; do if grep -q '"id":"ci-probe"' /tmp/ale-capture.jsonl 2>/dev/null; then exit 0 fi if ! docker inspect ac-worldserver >/dev/null 2>&1; then echo "worldserver container disappeared" docker compose ps -a docker compose logs ac-worldserver exit 1 fi state=$(docker inspect --format='{{.State.Status}}' ac-worldserver 2>/dev/null || true) if [ "$state" = "exited" ] || [ "$state" = "dead" ]; then docker compose logs ac-worldserver exit 1 fi sleep 2 done echo "Timed out waiting for ALE command round-trip" docker compose logs ac-worldserver cat /tmp/ale-capture-server.log || true cat /tmp/ale-capture.jsonl || true exit 1 - name: Verify ALE executes inside the real worldserver run: | cat /tmp/ale-capture.jsonl node - <<'NODE' const fs=require('fs'); const rows=fs.readFileSync('/tmp/ale-capture.jsonl','utf8').trim().split('\n').filter(Boolean).map(JSON.parse); const ready=rows.find(r=>r.kind==='event'&&r.payload?.kind==='bridge.ready'); if(!ready) throw new Error('ALE bridge.ready event never reached the receiver'); const result=rows.find(r=>r.kind==='result'&&r.payload?.id==='ci-probe'); if(!result) throw new Error('ALE operator probe never returned'); if(result.payload.ok!==true) throw new Error('ALE operator probe failed: '+JSON.stringify(result.payload)); const text=String(result.payload.result||''); if(!text.includes('core=')) throw new Error('Core identity missing from ALE probe: '+text); if(!text.toLowerCase().includes('lua=')) throw new Error('Lua engine identity missing from ALE probe: '+text); console.log('REAL WORLDSERVER PROBE:',text); NODE - name: Verify both modules are installed in the runtime working-directory: /tmp/acore run: | docker exec ac-worldserver sh -lc 'find /azerothcore/env/dist/etc -maxdepth 3 -type f \( -iname "*ale*" -o -iname "*playerbot*" \) -print' | tee /tmp/module-configs.txt grep -qi ale /tmp/module-configs.txt grep -qi playerbot /tmp/module-configs.txt docker compose logs ac-worldserver | grep -Ei 'ALE|playerbot|AzerothAI' | tail -200 || true - name: Save integration diagnostics if: always() run: | mkdir -p integration-artifacts cp /tmp/ale-capture.jsonl integration-artifacts/ 2>/dev/null || true cp /tmp/ale-capture-server.log integration-artifacts/ 2>/dev/null || true if [ -d "/tmp/acore" ]; then cd "/tmp/acore" docker compose ps -a > "${GITHUB_WORKSPACE}/integration-artifacts/docker-ps.txt" 2>&1 || true docker compose logs ac-worldserver > "${GITHUB_WORKSPACE}/integration-artifacts/worldserver.log" 2>&1 || true docker compose logs ac-authserver > "${GITHUB_WORKSPACE}/integration-artifacts/authserver.log" 2>&1 || true docker compose logs ac-db-import > "${GITHUB_WORKSPACE}/integration-artifacts/db-import.log" 2>&1 || true fi - name: Upload integration diagnostics if: always() uses: actions/upload-artifact@v4 with: name: wow-integration-diagnostics path: integration-artifacts/ if-no-files-found: ignore