new version commit

This commit is contained in:
mikx
2025-09-29 02:27:58 -04:00
commit 3e8d31e686
9244 changed files with 7357899 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
#!/usr/bin/env bash
# Set SUDO variable - one liner
SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "")
function inst_configureOS() {
echo "Platform: $OSTYPE"
case "$OSTYPE" in
solaris*) echo "Solaris is not supported yet" ;;
darwin*) source "$AC_PATH_INSTALLER/includes/os_configs/osx.sh" ;;
linux*)
# If $OSDISTRO is set, use this value (from config.sh)
if [ ! -z "$OSDISTRO" ]; then
DISTRO=$OSDISTRO
# If available, use LSB to identify distribution
elif command -v lsb_release >/dev/null 2>&1 ; then
DISTRO=$(lsb_release -is)
# Otherwise, use release info file
else
DISTRO=$(ls -d /etc/[A-Za-z]*[_-][rv]e[lr]* | grep -v "lsb" | cut -d'/' -f3 | cut -d'-' -f1 | cut -d'_' -f1)
fi
case $DISTRO in
# add here distro that are debian or ubuntu based
# TODO: find a better way, maybe checking the existance
# of a package manager
"neon" | "ubuntu" | "Ubuntu")
DISTRO="ubuntu"
;;
"debian" | "Debian")
DISTRO="debian"
;;
*)
echo "Distro: $DISTRO, is not supported. If your distribution is based on debian or ubuntu,
please set the 'OSDISTRO' environment variable to one of these distro (you can use config.sh file)"
;;
esac
DISTRO=${DISTRO,,}
echo "Distro: $DISTRO"
# TODO: implement different configurations by distro
source "$AC_PATH_INSTALLER/includes/os_configs/$DISTRO.sh"
;;
*bsd*) echo "BSD is not supported yet" ;;
msys*) source "$AC_PATH_INSTALLER/includes/os_configs/windows.sh" ;;
*) echo "This platform is not supported" ;;
esac
}
# Use the data/sql/create/create_mysql.sql to initialize the database
function inst_dbCreate() {
echo "Creating database..."
# Attempt to connect with MYSQL_ROOT_PASSWORD
if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
if $SUDO mysql -u root -p"$MYSQL_ROOT_PASSWORD" < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" 2>/dev/null; then
echo "Database created successfully."
return 0
else
echo "Failed to connect with provided password, falling back to interactive mode..."
fi
fi
# In CI environments or when no password is set, try without password first
if [[ "$CONTINUOUS_INTEGRATION" == "true" ]]; then
echo "CI environment detected, attempting connection without password..."
if $SUDO mysql -u root < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" 2>/dev/null; then
echo "Database created successfully."
return 0
else
echo "Failed to connect without password, falling back to interactive mode..."
fi
fi
# Try with password (interactive mode)
echo "Please enter your sudo and your MySQL root password if prompted."
$SUDO mysql -u root -p < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql"
if [ $? -ne 0 ]; then
echo "Database creation failed. Please check your MySQL server and credentials."
exit 1
fi
echo "Database created successfully."
}
function inst_updateRepo() {
cd "$AC_PATH_ROOT"
if [ ! -z $INSTALLER_PULL_FROM ]; then
git pull "$ORIGIN_REMOTE" "$INSTALLER_PULL_FROM"
else
git pull "$ORIGIN_REMOTE" $(git rev-parse --abbrev-ref HEAD)
fi
}
function inst_resetRepo() {
cd "$AC_PATH_ROOT"
git reset --hard $(git rev-parse --abbrev-ref HEAD)
git clean -f
}
function inst_compile() {
comp_configure
comp_build
}
function inst_cleanCompile() {
comp_clean
inst_compile
}
function inst_allInOne() {
inst_configureOS
inst_compile
inst_dbCreate
inst_download_client_data
}
############################################################
# Module helpers and dispatcher #
############################################################
# Returns the default branch name of a GitHub repo in the azerothcore org.
# If the API call fails, defaults to "master".
function inst_get_default_branch() {
local repo="$1"
local def
def=$(curl --silent "https://api.github.com/repos/azerothcore/${repo}" \
| "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.default_branch')
if [ -z "$def" ]; then
def="master"
fi
echo "$def"
}
# =============================================================================
# Module Management System
# =============================================================================
# Load the module manager functions from the dedicated modules-manager directory
source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh"
function inst_simple_restarter {
echo "Running $1 ..."
bash "$AC_PATH_APPS/startup-scripts/src/simple-restarter" "$AC_BINPATH_FULL" "$1"
echo
#disown -a
#jobs -l
}
function inst_download_client_data {
# change the following version when needed
local VERSION=v16
echo "#######################"
echo "Client data downloader"
echo "#######################"
# first check if it's defined in env, otherwise use the default
local path="${DATAPATH:-$AC_BINPATH_FULL}"
local zipPath="${DATAPATH_ZIP:-"$path/data.zip"}"
dataVersionFile="$path/data-version"
[ -f "$dataVersionFile" ] && source "$dataVersionFile"
# create the path if doesn't exists
mkdir -p "$path"
if [ "$VERSION" == "$INSTALLED_VERSION" ]; then
echo "Data $VERSION already installed. If you want to force the download remove the following file: $dataVersionFile"
return
fi
echo "Downloading client data in: $zipPath ..."
curl -L https://github.com/wowgaming/client-data/releases/download/$VERSION/data.zip > "$zipPath" \
&& echo "unzip downloaded file in $path..." && unzip -q -o "$zipPath" -d "$path/" \
&& echo "Remove downloaded file" && rm "$zipPath" \
&& echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile"
}

View File

@@ -0,0 +1,22 @@
[[ ${INSTALLER_GUARDYVAR:-} -eq 1 ]] && return || readonly INSTALLER_GUARDYVAR=1 # include it once
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd )
source "$CURRENT_PATH/../../bash_shared/includes.sh"
AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
J_PATH="$AC_PATH_DEPS/acore/joiner"
J_PATH_MODULES="$AC_PATH_MODULES"
source "$J_PATH/joiner.sh"
if [ -f "$AC_PATH_INSTALLER/config.sh" ]; then
source "$AC_PATH_INSTALLER/config.sh" # should overwrite previous
fi
source "$AC_PATH_APPS/compiler/includes/includes.sh"
source "$AC_PATH_DEPS/semver_bash/semver.sh"
source "$AC_PATH_INSTALLER/includes/functions.sh"

View File

@@ -0,0 +1,311 @@
# AzerothCore Module Manager
This directory contains the module management system for AzerothCore, providing advanced functionality for installing, updating, and managing server modules.
## 🚀 Features
- **Advanced Syntax**: Support for `repo[:dirname][@branch[:commit]]` format
- **Cross-Format Recognition**: Intelligent matching across URLs, SSH, and simple names
- **Custom Directory Naming**: Prevent conflicts with custom directory names
- **Duplicate Prevention**: Smart detection and prevention of duplicate installations
- **Multi-Host Support**: GitHub, GitLab, and other Git hosts
- **Module Exclusion**: Support for excluding modules via environment variable
- **Interactive Menu System**: Easy-to-use menu interface for module management
- **Colored Output**: Enhanced terminal output with color support (respects NO_COLOR)
- **Flat Directory Structure**: Uses flat module installation (no owner subfolders)
## 📁 File Structure
```
modules-manager/
├── modules.sh # Core module management functions
└── README.md # This documentation file
```
## 🔧 Module Specification Syntax
The module manager supports flexible syntax for specifying modules:
### New Syntax Format
```bash
repo[:dirname][@branch[:commit]]
```
### Examples
| Specification | Description |
|---------------|-------------|
| `mod-transmog` | Simple module name, uses default branch and directory |
| `mod-transmog:my-custom-dir` | Custom directory name |
| `mod-transmog@develop` | Specific branch |
| `mod-transmog:custom@develop:abc123` | Custom directory, branch, and commit |
| `https://github.com/owner/repo.git@main` | Full URL with branch |
| `git@github.com:owner/repo.git:custom-dir` | SSH URL with custom directory |
## 🎯 Usage Examples
### Installing Modules
```bash
# Simple module installation
./acore.sh module install mod-transmog
# Install with custom directory name
./acore.sh module install mod-transmog:my-transmog-dir
# Install specific branch
./acore.sh module install mod-transmog@develop
# Install with full specification
./acore.sh module install mod-transmog:custom-dir@develop:abc123
# Install from URL
./acore.sh module install https://github.com/azerothcore/mod-transmog.git@main
# Install multiple modules
./acore.sh module install mod-transmog mod-eluna:custom-eluna
# Install all modules from list
./acore.sh module install --all
```
### Updating Modules
```bash
# Update specific module
./acore.sh module update mod-transmog
# Update all modules
./acore.sh module update --all
# Update with branch specification
./acore.sh module update mod-transmog@develop
```
### Removing Modules
```bash
# Remove by simple name (cross-format recognition)
./acore.sh module remove mod-transmog
# Remove by URL (recognizes same module)
./acore.sh module remove https://github.com/azerothcore/mod-transmog.git
# Remove multiple modules
./acore.sh module remove mod-transmog mod-eluna
```
### Searching Modules
```bash
# Search for modules
./acore.sh module search transmog
# Search with multiple terms
./acore.sh module search auction house
# Search with input prompt
./acore.sh module search
```
### Listing Installed Modules
```bash
# List all installed modules
./acore.sh module list
```
### Interactive Menu
```bash
# Start interactive menu system
./acore.sh module
# Menu options:
# s - Search for available modules
# i - Install one or more modules
# u - Update installed modules
# r - Remove installed modules
# l - List installed modules
# h - Show detailed help
# q - Close this menu
```
## 🔍 Cross-Format Recognition
The system intelligently recognizes the same module across different specification formats:
```bash
# These all refer to the same module:
mod-transmog
azerothcore/mod-transmog
https://github.com/azerothcore/mod-transmog.git
git@github.com:azerothcore/mod-transmog.git
```
This allows:
- Installing with one format and removing with another
- Preventing duplicates regardless of specification format
- Consistent module tracking across different input methods
## 🛡️ Conflict Prevention
The system prevents common conflicts:
### Directory Conflicts
```bash
# If 'mod-transmog' directory already exists:
$ ./acore.sh module install mod-transmog:mod-transmog
Possible solutions:
1. Use a different directory name: mod-transmog:my-custom-name
2. Remove the existing directory first
3. Use the update command if this is the same module
```
### Duplicate Module Prevention
The system uses intelligent owner/name matching to prevent installing the same module multiple times, even when specified in different formats.
## 🚫 Module Exclusion
You can exclude modules from installation using the `MODULES_EXCLUDE_LIST` environment variable:
```bash
# Exclude specific modules (space-separated)
export MODULES_EXCLUDE_LIST="mod-test-module azerothcore/mod-dev-only"
./acore.sh module install --all # Will skip excluded modules
# Supports cross-format matching
export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git"
./acore.sh module install mod-transmog # Will be skipped as excluded
```
The exclusion system:
- Uses the same cross-format recognition as other module operations
- Works with all installation methods (`install`, `install --all`)
- Provides clear feedback when modules are skipped
- Supports URLs, owner/name format, and simple names
## 🎨 Color Support
The module manager provides enhanced terminal output with colors:
- **Info**: Cyan text for informational messages
- **Success**: Green text for successful operations
- **Warning**: Yellow text for warnings
- **Error**: Red text for errors
- **Headers**: Bold cyan text for section headers
Color support is automatically disabled when:
- Output is not to a terminal (piped/redirected)
- `NO_COLOR` environment variable is set
- Terminal doesn't support colors
You can force color output with:
```bash
export FORCE_COLOR=1
```
## 🔄 Integration
### Including in Scripts
```bash
# Source the module functions
source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh"
# Use module functions
inst_module_install "mod-transmog:custom-dir@develop"
```
### Testing
The module system is tested through the main installer test suite:
```bash
./apps/installer/test/test_module_commands.bats
```
## 📋 Module List Format
Modules are tracked in `conf/modules.list` with the format:
```
# Comments start with #
repo_reference branch commit
# Examples:
azerothcore/mod-transmog master abc123def456
https://github.com/custom/mod-custom.git develop def456abc789
mod-eluna:custom-eluna-dir main 789abc123def
```
The list maintains:
- **Alphabetical ordering** by normalized owner/name for consistency
- **Original format preservation** of how modules were specified
- **Automatic deduplication** across different specification formats
- **Custom directory tracking** when specified
## 🔧 Configuration
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `MODULES_LIST_FILE` | Override default modules list path | `$AC_PATH_ROOT/conf/modules.list` |
| `MODULES_EXCLUDE_LIST` | Space-separated list of modules to exclude | - |
| `J_PATH_MODULES` | Modules installation directory | `$AC_PATH_ROOT/modules` |
| `AC_PATH_ROOT` | AzerothCore root path | - |
| `NO_COLOR` | Disable colored output | - |
| `FORCE_COLOR` | Force colored output even when not TTY | - |
### Default Paths
- **Modules list**: `$AC_PATH_ROOT/conf/modules.list`
- **Installation directory**: `$J_PATH_MODULES` (flat structure, no owner subfolders)
## 🏗️ Architecture
### Core Functions
| Function | Purpose |
|----------|---------|
| `inst_module()` | Main dispatcher and interactive menu |
| `inst_parse_module_spec()` | Parse advanced module syntax |
| `inst_extract_owner_name()` | Normalize modules for cross-format recognition |
| `inst_mod_list_*()` | Module list management (read/write/update) |
| `inst_module_*()` | Module operations (install/update/remove/search) |
### Key Features
- **Flat Directory Structure**: All modules install directly under `modules/` without owner subdirectories
- **Smart Conflict Detection**: Prevents directory name conflicts with helpful suggestions
- **Cross-Platform Compatibility**: Works on Linux, macOS, and Windows (Git Bash)
- **Version Compatibility**: Checks `acore-module.json` for AzerothCore version compatibility
- **Git Integration**: Uses Joiner system for Git repository management
### Debug Mode
For debugging module operations, you can examine the generated commands:
```bash
# Check what Joiner commands would be executed
tail -f /tmp/joiner_called.txt # In test environments
```
## 🤝 Contributing
When modifying the module manager:
1. **Maintain backwards compatibility** with existing module list format
2. **Update tests** in `test_module_commands.bats` for new functionality
3. **Update this documentation** for any new features or changes
4. **Test cross-format recognition** thoroughly across all supported formats
5. **Ensure helpful error messages** for common user mistakes
6. **Test exclusion functionality** with various module specification formats
7. **Verify color output** works correctly in different terminal environments
### Testing Guidelines
```bash
# Run all module-related tests
cd apps/installer
bats test/test_module_commands.bats
# Test with different environments
NO_COLOR=1 ./acore.sh module list
FORCE_COLOR=1 ./acore.sh module help
```

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
source "$CURRENT_PATH/modules.sh"
inst_module "$@"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Set SUDO variable - one liner
SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "")
if ! command -v lsb_release &>/dev/null ; then
$SUDO apt-get install -y lsb-release
fi
DEBIAN_VERSION=$(lsb_release -sr)
DEBIAN_VERSION_MIN="12"
if [[ $DEBIAN_VERSION -lt $DEBIAN_VERSION_MIN ]]; then
echo "########## ########## ##########"
echo ""
echo " using unsupported Debian version" $DEBIAN_VERSION
echo " please update to Debian" $DEBIAN_VERSION_MIN "or later"
echo ""
echo "########## ########## ##########"
fi
$SUDO apt-get update -y
$SUDO apt-get install -y gdbserver gdb unzip curl \
libncurses-dev libreadline-dev clang g++ \
gcc git cmake make ccache \
libssl-dev libbz2-dev \
libboost-all-dev gnupg wget jq screen tmux expect
VAR_PATH="$CURRENT_PATH/../../../../var"
# run noninteractive install for MYSQL 8.4 LTS
wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH"
DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb"
$SUDO apt-get update
DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server libmysqlclient-dev

View File

@@ -0,0 +1,34 @@
##########################################
## workaround for python upgrade issue https://github.com/actions/runner-images/issues/6817
rm /usr/local/bin/2to3 || true
rm /usr/local/bin/2to3-3.10 || true
rm /usr/local/bin/2to3-3.11 || true
rm /usr/local/bin/2to3-3.12 || true
rm /usr/local/bin/idle3 || true
rm /usr/local/bin/idle3.10 || true
rm /usr/local/bin/idle3.11 || true
rm /usr/local/bin/idle3.12 || true
rm /usr/local/bin/pydoc3 || true
rm /usr/local/bin/pydoc3.10 || true
rm /usr/local/bin/pydoc3.11 || true
rm /usr/local/bin/pydoc3.12 || true
rm /usr/local/bin/python3 || true
rm /usr/local/bin/python3.10 || true
rm /usr/local/bin/python3.11 || true
rm /usr/local/bin/python3.12 || true
rm /usr/local/bin/python3-config || true
rm /usr/local/bin/python3.10-config || true
rm /usr/local/bin/python3.11-config || true
rm /usr/local/bin/python3.12-config || true
##########################################
brew update
##########################################
## workaround for cmake already being installed in the github runners
if ! command -v cmake &>/dev/null ; then
brew install cmake
fi
##########################################
brew install openssl@3 readline boost bash-completion curl unzip mysql ccache expect tmux screen jq

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Set SUDO variable - one liner
SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "")
if ! command -v lsb_release &>/dev/null ; then
$SUDO apt-get install -y lsb-release
fi
UBUNTU_VERSION=$(lsb_release -sr);
case $UBUNTU_VERSION in
"22.04")
;;
"24.04")
;;
*)
echo "########## ########## ##########"
echo ""
echo " using unsupported Ubuntu version " $UBUNTU_VERSION
echo " please update to Ubuntu 22.04 or later"
echo ""
echo "########## ########## ##########"
;;
esac
$SUDO apt update
# shared deps
DEBIAN_FRONTEND="noninteractive" $SUDO \
apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev make unzip jq screen tmux \
libreadline-dev libncurses5-dev libncursesw5-dev libbz2-dev git gcc g++ libssl-dev \
libncurses-dev libboost-all-dev gdb gdbserver expect
VAR_PATH="$CURRENT_PATH/../../../../var"
# Do not install MySQL if we are in docker (It will be used a docker container instead) or we are explicitly skipping it.
if [[ $DOCKER != 1 && $SKIP_MYSQL_INSTALL != 1 ]]; then
# run noninteractive install for MYSQL 8.4 LTS
wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH"
DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb"
$SUDO apt-get update
DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server
fi
if [[ $CONTINUOUS_INTEGRATION ]]; then
$SUDO systemctl enable mysql.service
$SUDO systemctl start mysql.service
fi

View File

@@ -0,0 +1,30 @@
# install chocolatey before
# powershell.exe -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
# install automatically following packages:
# cmake
# git
# microsoft-build-tools
# mysql
INSTALL_ARGS=""
if [[ $CONTINUOUS_INTEGRATION ]]; then
INSTALL_ARGS=" --no-progress "
else
{ # try
choco uninstall -y -n cmake.install cmake # needed to make sure that following install set the env properly
} || { # catch
echo "nothing to do"
}
choco install -y --skip-checksums $INSTALL_ARGS git visualstudio2022community
fi
choco install -y --skip-checksums $INSTALL_ARGS cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System'
choco install -y --skip-checksums $INSTALL_ARGS visualstudio2022-workload-nativedesktop
choco install -y --skip-checksums $INSTALL_ARGS openssl --force --version=3.5.2
choco install -y --skip-checksums $INSTALL_ARGS boost-msvc-14.3 --force --version=1.87.0
choco install -y --skip-checksums $INSTALL_ARGS mysql --force --version=8.4.4

107
apps/installer/main.sh Normal file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env bash
# AzerothCore Dashboard Script
#
# This script provides an interactive menu system for AzerothCore management
# using the unified menu system library.
#
# Usage:
# ./acore.sh - Interactive mode with numeric and text selection
# ./acore.sh <command> [args] - Direct command execution (only text commands, no numbers)
#
# Interactive Mode:
# - Select options by number (1, 2, 3...), command name (init, compiler, etc.),
# or short alias (i, c, etc.)
# - All selection methods work in interactive mode
#
# Direct Command Mode:
# - Only command names and short aliases are accepted (e.g., './acore.sh compiler build', './acore.sh c build')
# - Numeric selection is disabled to prevent confusion with command arguments
# - Examples: './acore.sh init', './acore.sh compiler clean', './acore.sh module install mod-name'
#
# Menu System:
# - Uses unified menu system from bash_shared/menu_system.sh
# - Single source of truth for menu definitions
# - Consistent behavior across all AzerothCore tools
CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_PATH/includes/includes.sh"
source "$AC_PATH_APPS/bash_shared/menu_system.sh"
# Menu: single ordered source of truth (no functions in strings)
# Format: "key|short|description"
menu_items=(
"init|i|First Installation"
"install-deps|d|Configure OS dep"
"pull|u|Update Repository"
"reset|r|Reset & Clean Repository"
"compiler|c|Run compiler tool"
"module|m|Module manager (search/install/update/remove)"
"client-data|gd|download client data from github repository (beta)"
"run-worldserver|rw|execute a simple restarter for worldserver"
"run-authserver|ra|execute a simple restarter for authserver"
"docker|dr|Run docker tools"
"version|v|Show AzerothCore version"
"service-manager|sm|Run service manager to run authserver and worldserver in background"
"quit|q|Exit from this menu"
)
# Menu command handler - called by menu system for each command
function handle_menu_command() {
local key="$1"
shift
case "$key" in
"init")
inst_allInOne
;;
"install-deps")
inst_configureOS
;;
"pull")
inst_updateRepo
;;
"reset")
inst_resetRepo
;;
"compiler")
bash "$AC_PATH_APPS/compiler/compiler.sh" "$@"
;;
"module")
bash "$AC_PATH_APPS/installer/includes/modules-manager/module-main.sh" "$@"
;;
"client-data")
inst_download_client_data
;;
"run-worldserver")
inst_simple_restarter worldserver
;;
"run-authserver")
inst_simple_restarter authserver
;;
"docker")
DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@"
exit
;;
"version")
printf "AzerothCore Rev. %s\n" "$ACORE_VERSION"
exit
;;
"service-manager")
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@"
exit
;;
"quit")
echo "Goodbye!"
exit
;;
*)
echo "Invalid option. Use --help to see available commands."
return 1
;;
esac
}
# Run the menu system
menu_run_with_items "ACORE DASHBOARD" handle_menu_command -- "${menu_items[@]}" -- "$@"

View File

@@ -0,0 +1,14 @@
# BATS Test Configuration
# Set test timeout (in seconds)
export BATS_TEST_TIMEOUT=30
# Enable verbose output for debugging
export BATS_VERBOSE_RUN=1
# Test output format
export BATS_FORMATTER=pretty
# Enable colored output
export BATS_NO_PARALLELIZE_ACROSS_FILES=1
export BATS_NO_PARALLELIZE_WITHIN_FILE=1

View File

@@ -0,0 +1,755 @@
#!/usr/bin/env bats
# Tests for installer module commands (search/install/update/remove)
# Focused on installer:module install behavior using a mocked joiner
load '../../test-framework/bats_libs/acore-support'
load '../../test-framework/bats_libs/acore-assert'
setup() {
acore_test_setup
# Point to the installer src directory (not needed in this test)
# Set installer/paths environment for the test
export AC_PATH_APPS="$TEST_DIR/apps"
export AC_PATH_ROOT="$TEST_DIR"
export AC_PATH_DEPS="$TEST_DIR/deps"
export AC_PATH_MODULES="$TEST_DIR/modules"
export MODULES_LIST_FILE="$TEST_DIR/conf/modules.list"
# Create stubbed deps: joiner.sh (sourced by includes) and semver
mkdir -p "$TEST_DIR/deps/acore/joiner"
cat > "$TEST_DIR/deps/acore/joiner/joiner.sh" << 'EOF'
#!/usr/bin/env bash
# Stub joiner functions used by installer
Joiner:add_repo() {
# arguments: url name branch basedir
echo "ADD $@" > "$TEST_DIR/joiner_called.txt"
return 0
}
Joiner:upd_repo() {
echo "UPD $@" > "$TEST_DIR/joiner_called.txt"
return 0
}
Joiner:remove() {
echo "REM $@" > "$TEST_DIR/joiner_called.txt"
return 0
}
EOF
chmod +x "$TEST_DIR/deps/acore/joiner/joiner.sh"
mkdir -p "$TEST_DIR/deps/semver_bash"
# Minimal semver stub
cat > "$TEST_DIR/deps/semver_bash/semver.sh" << 'EOF'
#!/usr/bin/env bash
# semver stub
semver::satisfies() { return 0; }
EOF
chmod +x "$TEST_DIR/deps/semver_bash/semver.sh"
# Provide a minimal compiler includes file expected by installer
mkdir -p "$TEST_DIR/apps/compiler/includes"
touch "$TEST_DIR/apps/compiler/includes/includes.sh"
# Provide minimal bash_shared includes to satisfy installer include
mkdir -p "$TEST_DIR/apps/bash_shared"
cat > "$TEST_DIR/apps/bash_shared/includes.sh" << 'EOF'
#!/usr/bin/env bash
# minimal stub
EOF
# Copy the menu system needed by modules.sh
cp "$AC_TEST_ROOT/apps/bash_shared/menu_system.sh" "$TEST_DIR/apps/bash_shared/"
# Copy the real installer app into the test apps dir
mkdir -p "$TEST_DIR/apps"
cp -r "$(cd "$AC_TEST_ROOT/apps/installer" && pwd)" "$TEST_DIR/apps/installer"
}
teardown() {
acore_test_teardown
}
@test "module install should call joiner and record entry in modules list" {
cd "$TEST_DIR"
# Source installer includes and call the install function directly to avoid menu interaction
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install example-module@main:abcd1234"
# Check that joiner was called
[ -f "$TEST_DIR/joiner_called.txt" ]
grep -q "ADD" "$TEST_DIR/joiner_called.txt"
# Check modules list was created and contains the repo_ref and branch
[ -f "$TEST_DIR/conf/modules.list" ]
grep -q "azerothcore/example-module main" "$TEST_DIR/conf/modules.list"
}
@test "module install with owner/name format should work" {
cd "$TEST_DIR"
# Test with owner/name format
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install myorg/mymodule"
# Check that joiner was called with correct URL
[ -f "$TEST_DIR/joiner_called.txt" ]
grep -q "ADD https://github.com/myorg/mymodule mymodule" "$TEST_DIR/joiner_called.txt"
# Check modules list contains the entry
[ -f "$TEST_DIR/conf/modules.list" ]
grep -q "myorg/mymodule" "$TEST_DIR/conf/modules.list"
}
@test "module remove should call joiner remove and update modules list" {
cd "$TEST_DIR"
# First install a module
bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install test-module"
# Then remove it
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_remove test-module"
# Check that joiner remove was called
[ -f "$TEST_DIR/joiner_called.txt" ]
# With flat structure, basedir is empty; ensure name is present
grep -q "REM test-module" "$TEST_DIR/joiner_called.txt"
# Check modules list no longer contains the entry
[ -f "$TEST_DIR/conf/modules.list" ]
! grep -q "azerothcore/test-module" "$TEST_DIR/conf/modules.list"
}
# Tests for intelligent module management (duplicate prevention and cross-format removal)
@test "inst_extract_owner_name should extract owner/name from various formats" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test simple name
run inst_extract_owner_name "mod-transmog"
[ "$output" = "azerothcore/mod-transmog" ]
# Test owner/name format
run inst_extract_owner_name "azerothcore/mod-transmog"
[ "$output" = "azerothcore/mod-transmog" ]
# Test HTTPS URL
run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git"
[ "$output" = "azerothcore/mod-transmog" ]
# Test SSH URL
run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git"
[ "$output" = "azerothcore/mod-transmog" ]
# Test GitLab URL
run inst_extract_owner_name "https://gitlab.com/myorg/mymodule.git"
[ "$output" = "myorg/mymodule" ]
}
@test "inst_extract_owner_name should handle URLs with ports correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test HTTPS URL with port
run inst_extract_owner_name "https://example.com:8080/user/repo.git"
[ "$output" = "user/repo" ]
# Test SSH URL with port
run inst_extract_owner_name "ssh://git@example.com:2222/owner/module"
[ "$output" = "owner/module" ]
# Test URL with port and custom directory (should ignore the directory part)
run inst_extract_owner_name "https://gitlab.internal:9443/team/project.git:custom-dir"
[ "$output" = "team/project" ]
# Test complex URL with port (should extract owner/name correctly)
run inst_extract_owner_name "https://git.company.com:8443/department/awesome-module.git"
[ "$output" = "department/awesome-module" ]
}
@test "duplicate module entries should be prevented across different formats" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Add module via simple name
inst_mod_list_upsert "mod-transmog" "master" "abc123"
# Verify it's in the list
grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list"
# Add same module via owner/name format - should replace, not duplicate
inst_mod_list_upsert "azerothcore/mod-transmog" "dev" "def456"
# Should only have one entry (the new one)
[ "$(grep -c "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list")" -eq 1 ]
grep -q "azerothcore/mod-transmog dev def456" "$TEST_DIR/conf/modules.list"
! grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list"
}
@test "module installed via URL should be recognized when checking with different formats" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Install via HTTPS URL
inst_mod_list_upsert "https://github.com/azerothcore/mod-transmog.git" "master" "abc123"
# Should be detected as installed using simple name
run inst_mod_is_installed "mod-transmog"
[ "$status" -eq 0 ]
# Should be detected as installed using owner/name
run inst_mod_is_installed "azerothcore/mod-transmog"
[ "$status" -eq 0 ]
# Should be detected as installed using SSH URL
run inst_mod_is_installed "git@github.com:azerothcore/mod-transmog.git"
[ "$status" -eq 0 ]
# Non-existent module should not be detected
run inst_mod_is_installed "mod-nonexistent"
[ "$status" -ne 0 ]
}
@test "module installed via URL with port should be recognized correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Install via URL with port
inst_mod_list_upsert "https://gitlab.internal:9443/myorg/my-module.git" "master" "abc123"
# Should be detected as installed using normalized owner/name
run inst_mod_is_installed "myorg/my-module"
[ "$status" -eq 0 ]
# Should be detected when checking with different URL format
run inst_mod_is_installed "ssh://git@gitlab.internal:9443/myorg/my-module"
[ "$status" -eq 0 ]
# Should be detected when checking with custom directory syntax
run inst_mod_is_installed "myorg/my-module:custom-dir"
[ "$status" -eq 0 ]
# Different module should not be detected
run inst_mod_is_installed "myorg/different-module"
[ "$status" -ne 0 ]
}
@test "cross-format module removal should work" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Install via SSH URL
inst_mod_list_upsert "git@github.com:azerothcore/mod-transmog.git" "master" "abc123"
# Verify it's installed
grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list"
# Remove using simple name
inst_mod_list_remove "mod-transmog"
# Should be completely removed
! grep -q "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list"
! grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list"
}
@test "module installation should prevent duplicates when already installed" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Install via simple name first
inst_mod_list_upsert "mod-worldchat" "master" "abc123"
# Try to install same module via URL - should detect it's already installed
run inst_mod_is_installed "https://github.com/azerothcore/mod-worldchat.git"
[ "$status" -eq 0 ]
# Add via URL should replace the existing entry
inst_mod_list_upsert "https://github.com/azerothcore/mod-worldchat.git" "dev" "def456"
# Should only have one entry
[ "$(grep -c "azerothcore/mod-worldchat" "$TEST_DIR/conf/modules.list")" -eq 1 ]
grep -q "https://github.com/azerothcore/mod-worldchat.git dev def456" "$TEST_DIR/conf/modules.list"
}
@test "module update --all uses flat structure (no branch subfolders)" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Prepare modules.list with one entry and a matching local directory
mkdir -p "$TEST_DIR/conf"
echo "azerothcore/mod-transmog master abc123" > "$TEST_DIR/conf/modules.list"
mkdir -p "$TEST_DIR/modules/mod-transmog"
# Run update all
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update --all"
# Verify Joiner:upd_repo received flat structure args (no basedir)
[ -f "$TEST_DIR/joiner_called.txt" ]
grep -q "UPD https://github.com/azerothcore/mod-transmog mod-transmog master" "$TEST_DIR/joiner_called.txt"
}
@test "module update specific uses flat structure with override branch" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Create local directory so update proceeds
mkdir -p "$TEST_DIR/modules/mymodule"
# Run update specifying owner/name and branch
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update myorg/mymodule@dev"
# Should call joiner with name 'mymodule' and branch 'dev' (no basedir)
[ -f "$TEST_DIR/joiner_called.txt" ]
grep -q "UPD https://github.com/myorg/mymodule mymodule dev" "$TEST_DIR/joiner_called.txt"
}
@test "custom directory names should work with new syntax" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test parsing with custom directory name
run inst_parse_module_spec "mod-transmog:my-custom-dir@develop:abc123"
[ "$status" -eq 0 ]
# Should output: repo_ref owner name branch commit url dirname
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "azerothcore/mod-transmog" ]
[ "$owner" = "azerothcore" ]
[ "$name" = "mod-transmog" ]
[ "$branch" = "develop" ]
[ "$commit" = "abc123" ]
[ "$url" = "https://github.com/azerothcore/mod-transmog" ]
[ "$dirname" = "my-custom-dir" ]
}
@test "directory conflict detection should work" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Create a fake existing directory
mkdir -p "$TEST_DIR/modules/existing-dir"
# Should detect conflict
run inst_check_module_conflict "existing-dir" "mod-test"
[ "$status" -eq 1 ]
[[ "$output" =~ "Directory 'existing-dir' already exists" ]]
[[ "$output" =~ "Use a different directory name: mod-test:my-custom-name" ]]
# Should not detect conflict for non-existing directory
run inst_check_module_conflict "non-existing-dir" "mod-test"
[ "$status" -eq 0 ]
}
@test "module update should work with custom directories" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# First add module with custom directory to list
inst_mod_list_upsert "azerothcore/mod-transmog:custom-dir" "master" "abc123"
# Create fake module directory structure
mkdir -p "$TEST_DIR/modules/custom-dir/.git"
echo "ref: refs/heads/master" > "$TEST_DIR/modules/custom-dir/.git/HEAD"
# Mock git commands in the fake module directory
cat > "$TEST_DIR/modules/custom-dir/.git/config" << 'EOF'
[core]
repositoryformatversion = 0
filemode = true
bare = false
[remote "origin"]
url = https://github.com/azerothcore/mod-transmog
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
EOF
# Test update with custom directory should work
# Note: This would require more complex mocking for full integration test
# For now, just test the parsing recognizes the custom directory
run inst_parse_module_spec "azerothcore/mod-transmog:custom-dir"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$dirname" = "custom-dir" ]
}
@test "URL formats should be properly normalized" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test various URL formats produce same owner/name
run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog"
local url_format="$output"
run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git"
local url_git_format="$output"
run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git"
local ssh_format="$output"
run inst_extract_owner_name "azerothcore/mod-transmog"
local owner_name_format="$output"
run inst_extract_owner_name "mod-transmog"
local simple_format="$output"
# All should normalize to the same owner/name
[ "$url_format" = "azerothcore/mod-transmog" ]
[ "$url_git_format" = "azerothcore/mod-transmog" ]
[ "$ssh_format" = "azerothcore/mod-transmog" ]
[ "$owner_name_format" = "azerothcore/mod-transmog" ]
[ "$simple_format" = "azerothcore/mod-transmog" ]
}
# Tests for module exclusion functionality
@test "module exclusion should work with MODULES_EXCLUDE_LIST" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test exclusion with simple name
export MODULES_EXCLUDE_LIST="mod-test-module"
run inst_mod_is_excluded "mod-test-module"
[ "$status" -eq 0 ]
# Test exclusion with owner/name format
export MODULES_EXCLUDE_LIST="azerothcore/mod-test"
run inst_mod_is_excluded "mod-test"
[ "$status" -eq 0 ]
# Test exclusion with space-separated list
export MODULES_EXCLUDE_LIST="mod-one mod-two mod-three"
run inst_mod_is_excluded "mod-two"
[ "$status" -eq 0 ]
# Test exclusion with newline-separated list
export MODULES_EXCLUDE_LIST="
mod-alpha
mod-beta
mod-gamma
"
run inst_mod_is_excluded "mod-beta"
[ "$status" -eq 0 ]
# Test exclusion with URL format
export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git"
run inst_mod_is_excluded "mod-transmog"
[ "$status" -eq 0 ]
# Test non-excluded module
export MODULES_EXCLUDE_LIST="mod-other"
run inst_mod_is_excluded "mod-transmog"
[ "$status" -eq 1 ]
# Test empty exclusion list
unset MODULES_EXCLUDE_LIST
run inst_mod_is_excluded "mod-transmog"
[ "$status" -eq 1 ]
}
@test "install --all should skip excluded modules" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Setup modules list with excluded module
mkdir -p "$TEST_DIR/conf"
cat > "$TEST_DIR/conf/modules.list" << 'EOF'
azerothcore/mod-transmog master abc123
azerothcore/mod-excluded master def456
EOF
# Set exclusion list
export MODULES_EXCLUDE_LIST="mod-excluded"
# Mock the install process to capture output
run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install --all 2>&1"
# Should show that excluded module was skipped
[[ "$output" == *"azerothcore/mod-excluded"* && "$output" == *"Excluded by MODULES_EXCLUDE_LIST"* && "$output" == *"skipping"* ]]
}
@test "exclusion should work with multiple formats in same list" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test multiple exclusion formats
export MODULES_EXCLUDE_LIST="mod-test https://github.com/azerothcore/mod-transmog.git custom/mod-other"
run inst_mod_is_excluded "mod-test"
[ "$status" -eq 0 ]
run inst_mod_is_excluded "azerothcore/mod-transmog"
[ "$status" -eq 0 ]
run inst_mod_is_excluded "custom/mod-other"
[ "$status" -eq 0 ]
run inst_mod_is_excluded "mod-allowed"
[ "$status" -eq 1 ]
}
# Tests for color support functionality
@test "color functions should work correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test that print functions exist and work
run print_info "test message"
[ "$status" -eq 0 ]
run print_warn "test warning"
[ "$status" -eq 0 ]
run print_error "test error"
[ "$status" -eq 0 ]
run print_success "test success"
[ "$status" -eq 0 ]
run print_skip "test skip"
[ "$status" -eq 0 ]
run print_header "test header"
[ "$status" -eq 0 ]
}
@test "color support should respect NO_COLOR environment variable" {
cd "$TEST_DIR"
# Test with NO_COLOR set
export NO_COLOR=1
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Colors should be empty when NO_COLOR is set
[ -z "$C_RED" ]
[ -z "$C_GREEN" ]
[ -z "$C_RESET" ]
}
# Tests for interactive menu system
@test "module help should display comprehensive help" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
run inst_module_help
[ "$status" -eq 0 ]
# Should contain key sections
[[ "$output" =~ "Module Manager Help" ]]
[[ "$output" =~ "Usage:" ]]
[[ "$output" =~ "Module Specification Syntax:" ]]
[[ "$output" =~ "Examples:" ]]
}
@test "module list should show installed modules correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Setup modules list
mkdir -p "$TEST_DIR/conf"
cat > "$TEST_DIR/conf/modules.list" << 'EOF'
azerothcore/mod-transmog master abc123
custom/mod-test develop def456
EOF
run inst_module_list
[ "$status" -eq 0 ]
# Should show both modules
[[ "$output" =~ "mod-transmog" ]]
[[ "$output" =~ "custom/mod-test" ]]
[[ "$output" =~ "master" ]]
[[ "$output" =~ "develop" ]]
}
@test "module list should handle empty list gracefully" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Ensure empty modules list
mkdir -p "$TEST_DIR/conf"
touch "$TEST_DIR/conf/modules.list"
run inst_module_list
[ "$status" -eq 0 ]
[[ "$output" =~ "No modules installed" ]]
}
# Tests for advanced parsing edge cases
@test "parsing should handle complex URL formats" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test GitLab URL with custom directory and branch
run inst_parse_module_spec "https://gitlab.com/myorg/mymodule.git:custom-dir@develop:abc123"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "https://gitlab.com/myorg/mymodule.git" ]
[ "$owner" = "myorg" ]
[ "$name" = "mymodule" ]
[ "$branch" = "develop" ]
[ "$commit" = "abc123" ]
[ "$dirname" = "custom-dir" ]
}
@test "parsing should handle URLs with ports correctly (fix for port/dirname confusion)" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test HTTPS URL with port - should NOT treat port as dirname
run inst_parse_module_spec "https://example.com:8080/user/repo.git"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "https://example.com:8080/user/repo.git" ]
[ "$owner" = "user" ]
[ "$name" = "repo" ]
[ "$branch" = "-" ]
[ "$commit" = "-" ]
[ "$url" = "https://example.com:8080/user/repo.git" ]
[ "$dirname" = "repo" ] # Should default to repo name, NOT port number
}
@test "parsing should handle URLs with ports and custom directory correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test URL with port AND custom directory - should parse custom directory correctly
run inst_parse_module_spec "https://example.com:8080/user/repo.git:custom-dir"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "https://example.com:8080/user/repo.git" ]
[ "$owner" = "user" ]
[ "$name" = "repo" ]
[ "$branch" = "-" ]
[ "$commit" = "-" ]
[ "$url" = "https://example.com:8080/user/repo.git" ]
[ "$dirname" = "custom-dir" ] # Should be custom-dir, not port number
}
@test "parsing should handle SSH URLs with ports correctly" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test SSH URL with port
run inst_parse_module_spec "ssh://git@example.com:2222/user/repo"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ]
[ "$owner" = "user" ]
[ "$name" = "repo" ]
[ "$dirname" = "repo" ] # Should be repo name, not port number
}
@test "parsing should handle SSH URLs with ports and custom directory" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test SSH URL with port and custom directory
run inst_parse_module_spec "ssh://git@example.com:2222/user/repo:my-custom-dir@develop"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ]
[ "$owner" = "user" ]
[ "$name" = "repo" ]
[ "$branch" = "develop" ]
[ "$dirname" = "my-custom-dir" ]
}
@test "parsing should handle complex URLs with ports, custom dirs, and branches" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Test comprehensive URL with port, custom directory, branch, and commit
run inst_parse_module_spec "https://gitlab.example.com:9443/myorg/myrepo.git:custom-name@feature-branch:abc123def"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "https://gitlab.example.com:9443/myorg/myrepo.git" ]
[ "$owner" = "myorg" ]
[ "$name" = "myrepo" ]
[ "$branch" = "feature-branch" ]
[ "$commit" = "abc123def" ]
[ "$url" = "https://gitlab.example.com:9443/myorg/myrepo.git" ]
[ "$dirname" = "custom-name" ]
}
@test "URL port parsing regression test - ensure ports are not confused with directory names" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# These are the problematic cases that the fix addresses
local test_cases=(
"https://example.com:8080/repo.git"
"https://gitlab.internal:9443/group/project.git"
"ssh://git@server.com:2222/owner/repo"
"https://git.company.com:8443/team/module.git"
)
for spec in "${test_cases[@]}"; do
run inst_parse_module_spec "$spec"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
# Critical: dirname should NEVER be a port number
[[ ! "$dirname" =~ ^[0-9]+$ ]] || {
echo "FAIL: Port number '$dirname' incorrectly parsed as directory name for spec: $spec"
return 1
}
# dirname should be the repository name by default
local expected_name
if [[ "$spec" =~ /([^/]+)(\.git)?$ ]]; then
expected_name="${BASH_REMATCH[1]}"
expected_name="${expected_name%.git}"
fi
[ "$dirname" = "$expected_name" ] || {
echo "FAIL: Expected dirname '$expected_name' but got '$dirname' for spec: $spec"
return 1
}
done
}
@test "parsing should handle URL with custom directory but no branch" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
run inst_parse_module_spec "https://github.com/owner/repo.git:my-dir"
[ "$status" -eq 0 ]
IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output"
[ "$repo_ref" = "https://github.com/owner/repo.git" ]
[ "$dirname" = "my-dir" ]
[ "$branch" = "-" ]
}
@test "modules list should maintain alphabetical order" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
# Add modules in random order
inst_mod_list_upsert "zeta/mod-z" "master" "abc"
inst_mod_list_upsert "alpha/mod-a" "master" "def"
inst_mod_list_upsert "beta/mod-b" "master" "ghi"
# Read the list and verify alphabetical order
local entries=()
while read -r repo_ref branch commit; do
[[ -z "$repo_ref" ]] && continue
entries+=("$repo_ref")
done < <(inst_mod_list_read)
# Should be in alphabetical order by owner/name
[ "${entries[0]}" = "alpha/mod-a" ]
[ "${entries[1]}" = "beta/mod-b" ]
[ "${entries[2]}" = "zeta/mod-z" ]
}
@test "module dispatcher should handle unknown commands gracefully" {
cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh"
run inst_module "unknown-command"
[ "$status" -eq 1 ]
[[ "$output" =~ "Unknown module command" ]]
}