feat(config): add support for excluding modules during installation and updates (#22793)

This commit is contained in:
Yehonal
2025-09-06 11:22:22 +02:00
committed by GitHub
parent 725b475dd4
commit d3a6c09b31
4 changed files with 785 additions and 76 deletions

View File

@@ -9,6 +9,10 @@ This directory contains the module management system for AzerothCore, providing
- **Custom Directory Naming**: Prevent conflicts with custom directory names - **Custom Directory Naming**: Prevent conflicts with custom directory names
- **Duplicate Prevention**: Smart detection and prevention of duplicate installations - **Duplicate Prevention**: Smart detection and prevention of duplicate installations
- **Multi-Host Support**: GitHub, GitLab, and other Git hosts - **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 ## 📁 File Structure
@@ -100,10 +104,33 @@ repo[:dirname][@branch[:commit]]
# Search with multiple terms # Search with multiple terms
./acore.sh module search auction house ./acore.sh module search auction house
# Show all available modules # Search with input prompt
./acore.sh module search ./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 ## 🔍 Cross-Format Recognition
The system intelligently recognizes the same module across different specification formats: The system intelligently recognizes the same module across different specification formats:
@@ -129,13 +156,55 @@ The system prevents common conflicts:
```bash ```bash
# If 'mod-transmog' directory already exists: # If 'mod-transmog' directory already exists:
$ ./acore.sh module install mod-transmog:mod-transmog $ ./acore.sh module install mod-transmog:mod-transmog
Error: Directory 'mod-transmog' already exists.
Possible solutions: Possible solutions:
1. Use a different directory name: mod-transmog:my-custom-name 1. Use a different directory name: mod-transmog:my-custom-name
2. Remove the existing directory first 2. Remove the existing directory first
3. Use the update command if this is the same module 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 ## 🔄 Integration
### Including in Scripts ### Including in Scripts
@@ -165,24 +234,78 @@ azerothcore/mod-transmog master abc123def456
https://github.com/custom/mod-custom.git develop def456abc789 https://github.com/custom/mod-custom.git develop def456abc789
mod-eluna:custom-eluna-dir main 789abc123def 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 ## 🔧 Configuration
### Environment Variables ### Environment Variables
- `MODULES_LIST_FILE`: Override default modules list path
- `J_PATH_MODULES`: Modules installation directory | Variable | Description | Default |
- `AC_PATH_ROOT`: AzerothCore root path |----------|-------------|---------|
| `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 ### Default Paths
- Modules list: `$AC_PATH_ROOT/conf/modules.list` - **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 ## 🤝 Contributing
When modifying the module manager: When modifying the module manager:
1. Maintain backwards compatibility 1. **Maintain backwards compatibility** with existing module list format
2. Update tests in `test_module_commands.bats` 2. **Update tests** in `test_module_commands.bats` for new functionality
3. Update this documentation 3. **Update this documentation** for any new features or changes
4. Test cross-format recognition thoroughly 4. **Test cross-format recognition** thoroughly across all supported formats
5. Ensure helpful error messages 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

@@ -28,6 +28,49 @@ source "$CURRENT_PATH/../../../bash_shared/includes.sh"
source "$CURRENT_PATH/../includes.sh" source "$CURRENT_PATH/../includes.sh"
source "$AC_PATH_APPS/bash_shared/menu_system.sh" source "$AC_PATH_APPS/bash_shared/menu_system.sh"
# -----------------------------------------------------------------------------
# Color support (disabled when not a TTY or NO_COLOR is set)
# -----------------------------------------------------------------------------
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
if command -v tput >/dev/null 2>&1; then
_ac_cols=$(tput colors 2>/dev/null || echo 0)
else
_ac_cols=0
fi
else
_ac_cols=0
fi
if [ "${FORCE_COLOR:-}" != "" ] || [ "${_ac_cols}" -ge 8 ]; then
C_RESET='\033[0m'
C_BOLD='\033[1m'
C_DIM='\033[2m'
C_RED='\033[31m'
C_GREEN='\033[32m'
C_YELLOW='\033[33m'
C_BLUE='\033[34m'
C_MAGENTA='\033[35m'
C_CYAN='\033[36m'
else
C_RESET=''
C_BOLD=''
C_DIM=''
C_RED=''
C_GREEN=''
C_YELLOW=''
C_BLUE=''
C_MAGENTA=''
C_CYAN=''
fi
# Simple helpers for consistent colored output
function print_info() { printf "%b\n" "${C_CYAN}$*${C_RESET}"; }
function print_warn() { printf "%b\n" "${C_YELLOW}$*${C_RESET}"; }
function print_error() { printf "%b\n" "${C_RED}$*${C_RESET}"; }
function print_success() { printf "%b\n" "${C_GREEN}$*${C_RESET}"; }
function print_skip() { printf "%b\n" "${C_BLUE}$*${C_RESET}"; }
function print_header() { printf "%b\n" "${C_BOLD}${C_CYAN}$*${C_RESET}"; }
# Module management menu definition # Module management menu definition
# Format: "key|short|description" # Format: "key|short|description"
module_menu_items=( module_menu_items=(
@@ -65,11 +108,11 @@ function handle_module_command() {
inst_module_help inst_module_help
;; ;;
"quit") "quit")
echo "Exiting module manager..." print_info "Exiting module manager..."
return 0 return 0
;; ;;
*) *)
echo "Invalid option. Use 'help' to see available commands." print_error "Invalid option. Use 'help' to see available commands."
return 1 return 1
;; ;;
esac esac
@@ -77,7 +120,7 @@ function handle_module_command() {
# Show detailed module help # Show detailed module help
function inst_module_help() { function inst_module_help() {
echo "AzerothCore Module Manager Help" print_header "AzerothCore Module Manager Help"
echo "===============================" echo "==============================="
echo "" echo ""
echo "Usage:" echo "Usage:"
@@ -106,20 +149,20 @@ function inst_module_help() {
# List installed modules # List installed modules
function inst_module_list() { function inst_module_list() {
echo "Installed Modules:" print_header "Installed Modules"
echo "==================" echo "=================="
local count=0 local count=0
while read -r repo_ref branch commit; do while read -r repo_ref branch commit; do
[[ -z "$repo_ref" ]] && continue [[ -z "$repo_ref" ]] && continue
count=$((count + 1)) count=$((count + 1))
echo " $count. $repo_ref ($branch)" printf " %s. %b (%s)%b\n" "$count" "${C_GREEN}${repo_ref}" "${branch}" "${C_RESET}"
if [[ "$commit" != "-" ]]; then if [[ "$commit" != "-" ]]; then
echo " Commit: $commit" printf " %bCommit:%b %s\n" "${C_DIM}" "${C_RESET}" "$commit"
fi fi
done < <(inst_mod_list_read) done < <(inst_mod_list_read)
if [[ $count -eq 0 ]]; then if [[ $count -eq 0 ]]; then
echo " No modules installed." print_warn " No modules installed."
fi fi
echo "" echo ""
} }
@@ -160,8 +203,7 @@ function inst_module() {
inst_module_list "${args[@]}" inst_module_list "${args[@]}"
;; ;;
*) *)
echo "Unknown module command: $cmd" print_error "Unknown module command: $cmd. Use 'help' to see available commands."
echo "Use 'help' to see available commands."
return 1 return 1
;; ;;
esac esac
@@ -188,20 +230,72 @@ function inst_parse_module_spec() {
# Parse the new syntax: repo[:dirname][@branch[:commit]] # Parse the new syntax: repo[:dirname][@branch[:commit]]
# First, extract custom directory name if present (format: repo:dirname@branch) # First, check if this is a URL (contains :// or starts with git@)
local is_url=0
if [[ "$spec" =~ :// ]] || [[ "$spec" =~ ^git@ ]]; then
is_url=1
fi
# Parse directory and branch differently for URLs vs simple names
local repo_with_branch="$spec" local repo_with_branch="$spec"
if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then if [[ $is_url -eq 1 ]]; then
repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" # For URLs, look for :dirname pattern, but be careful about ports
dirname="${BASH_REMATCH[2]}" # Strategy: only match :dirname if it's clearly after the repository path
# Look for :dirname patterns at the end, but not if it looks like a port
if [[ "$spec" =~ ^(.*\.git):([^@/:]+)(@.*)?$ ]]; then
# Repo ending with .git:dirname
repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
dirname="${BASH_REMATCH[2]}"
elif [[ "$spec" =~ ^(.*://[^/]+/[^:]*[^0-9]):([^@/:]+)(@.*)?$ ]]; then
# URL with path ending in non-digit:dirname (avoid matching ports)
repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
dirname="${BASH_REMATCH[2]}"
fi
# If no custom dirname found, repo_with_branch remains the original spec
else
# For simple names, use the original logic
if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then
repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
dirname="${BASH_REMATCH[2]}"
fi
fi fi
# Now parse branch and commit from the repo part # Now parse branch and commit from the repo part
if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then # Be careful not to confuse URL @ with branch @
repo_part="${BASH_REMATCH[1]}" if [[ "$repo_with_branch" =~ :// ]]; then
branch="${BASH_REMATCH[2]}" # For URLs, look for @ after the authority part
commit="${BASH_REMATCH[4]:-}" if [[ "$repo_with_branch" =~ ^[^/]*//[^/]+/.*@([^:]+)(:(.+))?$ ]]; then
# @ found in path part - treat as branch
repo_part="${repo_with_branch%@*}"
branch="${BASH_REMATCH[1]}"
commit="${BASH_REMATCH[3]:-}"
elif [[ "$repo_with_branch" =~ ^([^@]*@[^/]+/.*)@([^:]+)(:(.+))?$ ]]; then
# @ found after URL authority @ - treat as branch
repo_part="${BASH_REMATCH[1]}"
branch="${BASH_REMATCH[2]}"
commit="${BASH_REMATCH[4]:-}"
else
repo_part="$repo_with_branch"
fi
elif [[ "$repo_with_branch" =~ ^git@ ]]; then
# Git SSH format - look for @ after the initial git@host: part
if [[ "$repo_with_branch" =~ ^git@[^:]+:.*@([^:]+)(:(.+))?$ ]]; then
repo_part="${repo_with_branch%@*}"
branch="${BASH_REMATCH[1]}"
commit="${BASH_REMATCH[3]:-}"
else
repo_part="$repo_with_branch"
fi
else else
repo_part="$repo_with_branch" # Non-URL format - use original logic
if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then
repo_part="${BASH_REMATCH[1]}"
branch="${BASH_REMATCH[2]}"
commit="${BASH_REMATCH[4]:-}"
else
repo_part="$repo_with_branch"
fi
fi fi
# Normalize repo reference and extract owner/name. # Normalize repo reference and extract owner/name.
@@ -210,10 +304,39 @@ function inst_parse_module_spec() {
# If repo_ref is a URL, extract owner/name from path when possible # If repo_ref is a URL, extract owner/name from path when possible
if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then
# Extract owner/name (last two path components) # Handle various URL formats
owner_repo=$(echo "$repo_ref" | sed -E 's#(git@[^:]+:|https?://[^/]+/|ssh://[^/]+/)?(.*?)(\.git)?$#\2#') local path_part=""
owner="$(echo "$owner_repo" | awk -F'/' '{print $(NF-1)}')" if [[ "$repo_ref" =~ ^https?://[^/]+:?[0-9]*/(.+)$ ]]; then
name="$(echo "$owner_repo" | awk -F'/' '{print $NF}' | sed -E 's/\.git$//')" # HTTPS URL (with or without port)
path_part="${BASH_REMATCH[1]}"
elif [[ "$repo_ref" =~ ^ssh://.*@[^/]+:?[0-9]*/(.+)$ ]]; then
# SSH URL with user@host:port/path format
path_part="${BASH_REMATCH[1]}"
elif [[ "$repo_ref" =~ ^ssh://[^@/]+:?[0-9]*/(.+)$ ]]; then
# SSH URL with host:port/path format (no user@)
path_part="${BASH_REMATCH[1]}"
elif [[ "$repo_ref" =~ ^git@[^:]+:(.+)$ ]]; then
# Git SSH format (git@host:path)
path_part="${BASH_REMATCH[1]}"
fi
# Extract owner/name from path
if [[ -n "$path_part" ]]; then
# Remove .git suffix and any :dirname suffix
path_part="${path_part%.git}"
path_part="${path_part%:*}"
if [[ "$path_part" == *"/"* ]]; then
owner="$(echo "$path_part" | awk -F'/' '{print $(NF-1)}')"
name="$(echo "$path_part" | awk -F'/' '{print $NF}')"
else
owner="unknown"
name="$path_part"
fi
else
owner="unknown"
name="unknown"
fi
else else
owner_repo="$repo_ref" owner_repo="$repo_ref"
if [[ "$owner_repo" == *"/"* ]]; then if [[ "$owner_repo" == *"/"* ]]; then
@@ -266,21 +389,28 @@ function inst_extract_owner_name {
base_ref="${repo_ref%%:*}" base_ref="${repo_ref%%:*}"
fi fi
if [[ "$base_ref" =~ ^https?://github\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then # Handle various URL formats with possible ports
# HTTPS URL format - check this first before owner/name pattern if [[ "$base_ref" =~ ^https?://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then
# HTTPS URL format (with or without port) - matches github.com, gitlab.com, custom hosts
local owner="${BASH_REMATCH[1]}"
local name="${BASH_REMATCH[2]}" local name="${BASH_REMATCH[2]}"
name="${name%.git}" # Remove .git suffix if present name="${name%:*}" # Remove any :dirname suffix first
echo "${BASH_REMATCH[1]}/$name" name="${name%.git}" # Then remove .git suffix if present
elif [[ "$base_ref" =~ ^https?://gitlab\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then echo "$owner/$name"
# GitLab URL format elif [[ "$base_ref" =~ ^ssh://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then
# SSH URL format (with or without port)
local owner="${BASH_REMATCH[1]}"
local name="${BASH_REMATCH[2]}" local name="${BASH_REMATCH[2]}"
name="${name%.git}" # Remove .git suffix if present name="${name%:*}" # Remove any :dirname suffix first
echo "${BASH_REMATCH[1]}/$name" name="${name%.git}" # Then remove .git suffix if present
elif [[ "$base_ref" =~ ^git@github\.com:([^/]+)/([^/]+)(\.git)?$ ]]; then echo "$owner/$name"
# SSH URL format elif [[ "$base_ref" =~ ^git@[^:]+:([^/]+)/([^/?]+) ]]; then
# Git SSH format (git@host:owner/repo)
local owner="${BASH_REMATCH[1]}"
local name="${BASH_REMATCH[2]}" local name="${BASH_REMATCH[2]}"
name="${name%.git}" # Remove .git suffix if present name="${name%:*}" # Remove any :dirname suffix first
echo "${BASH_REMATCH[1]}/$name" name="${name%.git}" # Then remove .git suffix if present
echo "$owner/$name"
elif [[ "$base_ref" =~ ^[^/]+/[^/]+$ ]]; then elif [[ "$base_ref" =~ ^[^/]+/[^/]+$ ]]; then
# Format: owner/name (check after URL patterns) # Format: owner/name (check after URL patterns)
echo "$base_ref" echo "$base_ref"
@@ -330,6 +460,40 @@ function inst_mod_list_read() {
done < "$file" done < "$file"
} }
# Check whether a module spec matches the exclusion list.
# - Reads space/newline separated items from env var MODULES_EXCLUDE_LIST
# - Supports cross-format matching via inst_extract_owner_name
# Returns 0 if excluded, 1 otherwise.
function inst_mod_is_excluded() {
local spec="$1"
local target_owner_name
target_owner_name=$(inst_extract_owner_name "$spec")
# No exclusions configured
if [[ -z "${MODULES_EXCLUDE_LIST:-}" ]]; then
return 1
fi
# Split on default IFS (space, tab, newline)
local items=()
# Use mapfile to split MODULES_EXCLUDE_LIST on newlines; fallback to space if no newlines
if [[ "${MODULES_EXCLUDE_LIST}" == *$'\n'* ]]; then
mapfile -t items <<< "${MODULES_EXCLUDE_LIST}"
else
read -r -a items <<< "${MODULES_EXCLUDE_LIST}"
fi
local it it_owner
for it in "${items[@]}"; do
[[ -z "$it" ]] && continue
it_owner=$(inst_extract_owner_name "$it")
if [[ "$it_owner" == "$target_owner_name" ]]; then
return 0
fi
done
return 1
}
# Add or update an entry in the list: repo_ref branch commit # Add or update an entry in the list: repo_ref branch commit
# Removes any existing entries with the same owner/name to avoid duplicates # Removes any existing entries with the same owner/name to avoid duplicates
function inst_mod_list_upsert() { function inst_mod_list_upsert() {
@@ -448,12 +612,12 @@ function inst_check_module_conflict {
local repo_ref="$2" local repo_ref="$2"
if [ -d "$J_PATH_MODULES/$dirname" ]; then if [ -d "$J_PATH_MODULES/$dirname" ]; then
echo "Error: Directory '$dirname' already exists." print_error "Error: Directory '$dirname' already exists."
echo "Possible solutions:" print_warn "Possible solutions:"
echo " 1. Use a different directory name: $repo_ref:my-custom-name" echo " 1. Use a different directory name: $repo_ref:my-custom-name"
echo " 2. Remove the existing directory first" echo " 2. Remove the existing directory first"
echo " 3. Use the update command if this is the same module" echo " 3. Use the update command if this is the same module"
return 1 return 1
fi fi
return 0 return 0
} }
@@ -511,7 +675,7 @@ function inst_module_search {
local CATALOG_URL="https://www.azerothcore.org/data/catalogue.json" local CATALOG_URL="https://www.azerothcore.org/data/catalogue.json"
echo "Searching ${terms[*]}..." print_header "Searching ${terms[*]}..."
echo "" echo ""
# Build candidate list from catalogue (full_name = owner/repo) # Build candidate list from catalogue (full_name = owner/repo)
@@ -567,9 +731,9 @@ function inst_module_search {
read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${mod_full}/master/acore-module.json") read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${mod_full}/master/acore-module.json")
if [[ "$b" != "none" ]]; then if [[ "$b" != "none" ]]; then
echo "-> $mod (tested with AC version: $v)" printf "%b -> %b (tested with AC version: %s)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "$v" ""
else else
echo "-> $mod (NOTE: The module latest tested AC revision is Unknown)" printf "%b -> %b %b(NOTE: The module latest tested AC revision is Unknown)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "${C_YELLOW}" "${C_RESET}"
fi fi
done done
@@ -590,7 +754,7 @@ function inst_module_install {
local modules=("$@") local modules=("$@")
echo "Installing modules: ${modules[*]}" print_header "Installing modules: ${modules[*]}"
if $use_all; then if $use_all; then
# Install all modules from the list (respecting recorded branch and commit). # Install all modules from the list (respecting recorded branch and commit).
@@ -602,14 +766,18 @@ function inst_module_install {
local dup_error=0 local dup_error=0
while read -r repo_ref branch commit; do while read -r repo_ref branch commit; do
[ -z "$repo_ref" ] && continue [ -z "$repo_ref" ] && continue
# Skip excluded modules when checking duplicates
if inst_mod_is_excluded "$repo_ref"; then
continue
fi
parsed_output=$(inst_parse_module_spec "$repo_ref") parsed_output=$(inst_parse_module_spec "$repo_ref")
IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output"
# dirname defaults to repo name; flat install path uses dirname only # dirname defaults to repo name; flat install path uses dirname only
if [[ -n "${_seen[$dirname]:-}" ]]; then if [[ -n "${_seen[$dirname]:-}" ]]; then
echo "Error: duplicate module target directory '$dirname' detected in modules.list:" print_error "Error: duplicate module target directory '$dirname' detected in modules.list:"
echo " - ${_first[$dirname]}" echo " - ${_first[$dirname]}"
echo " - ${repo_ref}" echo " - ${repo_ref}"
echo "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt" print_warn "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt"
dup_error=1 dup_error=1
else else
_seen[$dirname]=1 _seen[$dirname]=1
@@ -623,11 +791,16 @@ function inst_module_install {
# Second pass: install in flat modules directory (no owner subfolders) # Second pass: install in flat modules directory (no owner subfolders)
while read -r repo_ref branch commit; do while read -r repo_ref branch commit; do
[ -z "$repo_ref" ] && continue [ -z "$repo_ref" ] && continue
# Skip excluded entries during installation
if inst_mod_is_excluded "$repo_ref"; then
print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)."
continue
fi
parsed_output=$(inst_parse_module_spec "$repo_ref") parsed_output=$(inst_parse_module_spec "$repo_ref")
IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output"
if [ -d "$J_PATH_MODULES/$dirname" ]; then if [ -d "$J_PATH_MODULES/$dirname" ]; then
echo "[$repo_ref] Already installed (skipping)." print_skip "[$repo_ref] Already installed (skipping)."
continue continue
fi fi
@@ -642,9 +815,9 @@ function inst_module_install {
local curCommit local curCommit
curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$branch" "$curCommit" inst_mod_list_upsert "$repo_ref" "$branch" "$curCommit"
echo "[$repo_ref] Installed." print_success "[$repo_ref] Installed."
else else
echo "[$repo_ref] Install failed." print_error "[$repo_ref] Install failed."
exit 1; exit 1;
fi fi
done < <(inst_mod_list_read) done < <(inst_mod_list_read)
@@ -663,7 +836,7 @@ function inst_module_install {
# Check if module is already installed (by owner/name matching) # Check if module is already installed (by owner/name matching)
existing_repo_ref=$(inst_mod_is_installed "$spec" || true) existing_repo_ref=$(inst_mod_is_installed "$spec" || true)
if [ -n "$existing_repo_ref" ]; then if [ -n "$existing_repo_ref" ]; then
echo "[$spec] Already installed as [$existing_repo_ref] (skipping)." print_skip "[$spec] Already installed as [$existing_repo_ref] (skipping)."
continue continue
fi fi
@@ -689,14 +862,14 @@ function inst_module_install {
fi fi
if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then
def="$(inst_get_default_branch "$repo_ref")" def="$(inst_get_default_branch "$repo_ref")"
echo "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)." print_warn "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)."
b="$def" b="$def"
fi fi
fi fi
# Use flat directory structure with custom directory name # Use flat directory structure with custom directory name
if [ -d "$J_PATH_MODULES/$dirname" ]; then if [ -d "$J_PATH_MODULES/$dirname" ]; then
echo "[$repo_ref] Already installed (skipping)." print_skip "[$repo_ref] Already installed (skipping)."
curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" inst_mod_list_upsert "$repo_ref" "$b" "$curCommit"
continue continue
@@ -709,14 +882,14 @@ function inst_module_install {
if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$override_commit" >/dev/null 2>&1; then if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$override_commit" >/dev/null 2>&1; then
git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$override_commit" git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$override_commit"
else else
echo "[$repo_ref] Warning: provided commit '$override_commit' not found; staying on branch '$b' HEAD." print_warn "[$repo_ref] provided commit '$override_commit' not found; staying on branch '$b' HEAD."
fi fi
fi fi
curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" inst_mod_list_upsert "$repo_ref" "$b" "$curCommit"
echo "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly." print_success "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly."
else else
echo "[$repo_ref] Install failed or module not found" print_error "[$repo_ref] Install failed or module not found"
exit 1; exit 1;
fi fi
done done
@@ -748,21 +921,26 @@ function inst_module_update {
local line repo_ref branch commit newCommit owner modname url dirname local line repo_ref branch commit newCommit owner modname url dirname
while read -r repo_ref branch commit; do while read -r repo_ref branch commit; do
[ -z "$repo_ref" ] && continue [ -z "$repo_ref" ] && continue
# Skip excluded modules during update --all
if inst_mod_is_excluded "$repo_ref"; then
print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)."
continue
fi
parsed_output=$(inst_parse_module_spec "$repo_ref") parsed_output=$(inst_parse_module_spec "$repo_ref")
IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output"
dirname="${dirname:-$modname}" dirname="${dirname:-$modname}"
if [ ! -d "$J_PATH_MODULES/$dirname/" ]; then if [ ! -d "$J_PATH_MODULES/$dirname/" ]; then
echo "[$repo_ref] Not installed locally, skipping." print_skip "[$repo_ref] Not installed locally, skipping."
continue continue
fi fi
if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then
newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit" inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit"
echo "[$repo_ref] Updated to latest commit on '$branch'." print_success "[$repo_ref] Updated to latest commit on '$branch'."
else else
echo "[$repo_ref] Cannot update" print_error "[$repo_ref] Cannot update"
fi fi
done < <(inst_mod_list_read) done < <(inst_mod_list_read)
else else
@@ -793,11 +971,11 @@ function inst_module_update {
fi fi
if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then
if branch=$(git -C "$J_PATH_MODULES/$dirname" rev-parse --abbrev-ref HEAD 2>/dev/null); then if branch=$(git -C "$J_PATH_MODULES/$dirname" rev-parse --abbrev-ref HEAD 2>/dev/null); then
echo "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'." print_warn "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'."
b="$branch" b="$branch"
else else
def="$(inst_get_default_branch "$repo_ref")" def="$(inst_get_default_branch "$repo_ref")"
echo "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'." print_warn "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'."
b="$def" b="$def"
fi fi
fi fi
@@ -806,12 +984,12 @@ function inst_module_update {
if Joiner:upd_repo "$url" "$dirname" "$b" ""; then if Joiner:upd_repo "$url" "$dirname" "$b" ""; then
newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$b" "$newCommit" inst_mod_list_upsert "$repo_ref" "$b" "$newCommit"
echo "[$repo_ref] Done, please re-run compiling and db assembly" print_success "[$repo_ref] Done, please re-run compiling and db assembly"
else else
echo "[$repo_ref] Cannot update" print_error "[$repo_ref] Cannot update"
fi fi
else else
echo "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)" print_error "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)"
fi fi
done done
fi fi
@@ -840,9 +1018,9 @@ function inst_module_remove {
dirname="${dirname:-$modname}" dirname="${dirname:-$modname}"
if Joiner:remove "$dirname" ""; then if Joiner:remove "$dirname" ""; then
inst_mod_list_remove "$repo_ref" inst_mod_list_remove "$repo_ref"
echo "[$repo_ref] Done, please re-run compiling" print_success "[$repo_ref] Done, please re-run compiling"
else else
echo "[$repo_ref] Cannot remove" print_error "[$repo_ref] Cannot remove"
fi fi
done done

View File

@@ -146,6 +146,27 @@ teardown() {
[ "$output" = "myorg/mymodule" ] [ "$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" { @test "duplicate module entries should be prevented across different formats" {
cd "$TEST_DIR" cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh" source "$TEST_DIR/apps/installer/includes/includes.sh"
@@ -189,6 +210,30 @@ teardown() {
[ "$status" -ne 0 ] [ "$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" { @test "cross-format module removal should work" {
cd "$TEST_DIR" cd "$TEST_DIR"
source "$TEST_DIR/apps/installer/includes/includes.sh" source "$TEST_DIR/apps/installer/includes/includes.sh"
@@ -355,3 +400,356 @@ EOF
[ "$owner_name_format" = "azerothcore/mod-transmog" ] [ "$owner_name_format" = "azerothcore/mod-transmog" ]
[ "$simple_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" ]]
}

10
conf/dist/config.sh vendored
View File

@@ -162,4 +162,14 @@ export CPUPROFILESIGNAL=${CPUPROFILESIGNAL:-12}
# Lines starting with '#' and empty lines are ignored. # Lines starting with '#' and empty lines are ignored.
export MODULES_LIST_FILE=${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"} export MODULES_LIST_FILE=${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"}
# Space/newline separated list of modules to exclude when using
# 'module install --all' and 'module update --all'. Items can be specified
# as simple names (e.g., mod-transmog), owner/name, or full URLs.
# Example:
# export MODULES_EXCLUDE_LIST="azerothcore/mod-transmog azerothcore/mod-autobalance"
export MODULES_EXCLUDE_LIST=""
NO_COLOR=${NO_COLOR:-}
FORCE_COLOR=${FORCE_COLOR:-}