cleanup: validation and integrations for importing data

This commit is contained in:
uprightbass360
2025-11-22 16:49:01 -05:00
committed by Deckard
parent e6231bb4a4
commit 6ddfe9b2c7
17 changed files with 6797 additions and 369 deletions

423
scripts/bash/lib/common.sh Normal file
View File

@@ -0,0 +1,423 @@
#!/bin/bash
#
# Common utilities library for AzerothCore RealmMaster scripts
# This library provides shared functions for environment variable reading,
# logging, error handling, and other common operations.
#
# Usage: source /path/to/scripts/bash/lib/common.sh
# Prevent multiple sourcing
if [ -n "${_COMMON_LIB_LOADED:-}" ]; then
return 0
fi
_COMMON_LIB_LOADED=1
# =============================================================================
# COLOR DEFINITIONS (Standardized across all scripts)
# =============================================================================
BLUE='\033[0;34m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Legacy color names for backward compatibility
COLOR_BLUE="$BLUE"
COLOR_GREEN="$GREEN"
COLOR_YELLOW="$YELLOW"
COLOR_RED="$RED"
COLOR_CYAN="$CYAN"
COLOR_RESET="$NC"
# =============================================================================
# LOGGING FUNCTIONS (Standardized with emoji)
# =============================================================================
# Log informational messages (blue with info icon)
info() {
printf '%b\n' "${BLUE} $*${NC}"
}
# Log success messages (green with checkmark)
ok() {
printf '%b\n' "${GREEN}$*${NC}"
}
# Log general messages (green, no icon - for clean output)
log() {
printf '%b\n' "${GREEN}$*${NC}"
}
# Log warning messages (yellow with warning icon)
warn() {
printf '%b\n' "${YELLOW}⚠️ $*${NC}"
}
# Log error messages (red with error icon, continues execution)
err() {
printf '%b\n' "${RED}$*${NC}" >&2
}
# Log fatal error and exit (red with error icon, exits with code 1)
fatal() {
printf '%b\n' "${RED}$*${NC}" >&2
exit 1
}
# =============================================================================
# ENVIRONMENT VARIABLE READING
# =============================================================================
# Read environment variable from .env file with fallback to default
# Handles various quote styles, comments, and whitespace
#
# Usage:
# read_env KEY [DEFAULT_VALUE]
# value=$(read_env "MYSQL_PASSWORD" "default_password")
#
# Features:
# - Reads from file specified by $ENV_PATH (or $DEFAULT_ENV_PATH)
# - Strips leading/trailing whitespace
# - Removes inline comments (everything after #)
# - Handles double quotes, single quotes, and unquoted values
# - Returns default value if key not found
# - Returns value from environment variable if already set
#
read_env() {
local key="$1"
local default="${2:-}"
local value=""
# Check if variable is already set in environment (takes precedence)
if [ -n "${!key:-}" ]; then
echo "${!key}"
return 0
fi
# Determine which .env file to use
local env_file="${ENV_PATH:-${DEFAULT_ENV_PATH:-}}"
# Read from .env file if it exists
if [ -f "$env_file" ]; then
# Extract value using grep and cut, handling various formats
value="$(grep -E "^${key}=" "$env_file" 2>/dev/null | tail -n1 | cut -d'=' -f2- | tr -d '\r')"
# Remove inline comments (everything after # that's not inside quotes)
# This is a simplified approach - doesn't handle quotes perfectly but works for most cases
value="$(echo "$value" | sed 's/[[:space:]]*#.*//' | sed 's/[[:space:]]*$//')"
# Strip quotes if present
if [[ "$value" == \"*\" && "$value" == *\" ]]; then
# Double quotes
value="${value:1:-1}"
elif [[ "$value" == \'*\' && "$value" == *\' ]]; then
# Single quotes
value="${value:1:-1}"
fi
fi
# Use default if still empty
if [ -z "${value:-}" ]; then
value="$default"
fi
printf '%s\n' "${value}"
}
# Read value from .env.template file (used during setup)
# This is similar to read_env but specifically for template files
#
# Usage:
# get_template_value KEY [TEMPLATE_FILE]
# value=$(get_template_value "MYSQL_PASSWORD")
#
get_template_value() {
local key="$1"
local template_file="${2:-${TEMPLATE_FILE:-${TEMPLATE_PATH:-.env.template}}}"
if [ ! -f "$template_file" ]; then
fatal "Template file not found: $template_file"
fi
# Extract value, handling variable expansion syntax like ${VAR:-default}
local value
local raw_line
raw_line=$(grep "^${key}=" "$template_file" 2>/dev/null | head -1)
if [ -z "$raw_line" ]; then
err "Key '$key' not found in template: $template_file"
return 1
fi
value="${raw_line#*=}"
value=$(echo "$value" | sed 's/^"\(.*\)"$/\1/')
# Handle ${VAR:-default} syntax by extracting the default value
if [[ "$value" =~ ^\$\{[^}]*:-([^}]*)\}$ ]]; then
value="${BASH_REMATCH[1]}"
fi
echo "$value"
}
# Update or add environment variable in .env file
# Creates file if it doesn't exist
#
# Usage:
# update_env_value KEY VALUE [ENV_FILE]
# update_env_value "MYSQL_PASSWORD" "new_password"
#
update_env_value() {
local key="$1"
local value="$2"
local env_file="${3:-${ENV_PATH:-${DEFAULT_ENV_PATH:-.env}}}"
[ -n "$env_file" ] || return 0
# Create file if it doesn't exist
if [ ! -f "$env_file" ]; then
printf '%s=%s\n' "$key" "$value" >> "$env_file"
return 0
fi
# Update existing or append new
if grep -q "^${key}=" "$env_file"; then
# Use platform-appropriate sed in-place editing
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s|^${key}=.*|${key}=${value}|" "$env_file"
else
sed -i "s|^${key}=.*|${key}=${value}|" "$env_file"
fi
else
printf '\n%s=%s\n' "$key" "$value" >> "$env_file"
fi
}
# =============================================================================
# VALIDATION & REQUIREMENTS
# =============================================================================
# Require command to be available in PATH, exit with error if not found
#
# Usage:
# require_cmd docker
# require_cmd python3 jq git
#
require_cmd() {
for cmd in "$@"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
fatal "Missing required command: $cmd"
fi
done
}
# Check if command exists (returns 0 if exists, 1 if not)
#
# Usage:
# if has_cmd docker; then
# echo "Docker is available"
# fi
#
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
# =============================================================================
# MYSQL/DATABASE HELPERS
# =============================================================================
# Execute MySQL command in Docker container
# Reads MYSQL_PW and container name from environment
#
# Usage:
# mysql_exec DATABASE_NAME < script.sql
# echo "SELECT 1;" | mysql_exec acore_auth
#
mysql_exec() {
local db="$1"
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
local container="${MYSQL_CONTAINER:-ac-mysql}"
docker exec -i "$container" mysql -uroot -p"$mysql_pw" "$db"
}
# Execute MySQL query and return result
# Outputs in non-tabular format suitable for parsing
#
# Usage:
# count=$(mysql_query "acore_characters" "SELECT COUNT(*) FROM characters")
#
mysql_query() {
local db="$1"
local query="$2"
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
local container="${MYSQL_CONTAINER:-ac-mysql}"
docker exec "$container" mysql -uroot -p"$mysql_pw" -N -B "$db" -e "$query" 2>/dev/null
}
# Check if MySQL container is healthy and accepting connections
#
# Usage:
# if mysql_is_ready; then
# echo "MySQL is ready"
# fi
#
mysql_is_ready() {
local container="${MYSQL_CONTAINER:-ac-mysql}"
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
docker exec "$container" mysqladmin ping -uroot -p"$mysql_pw" >/dev/null 2>&1
}
# Wait for MySQL to be ready with timeout
#
# Usage:
# mysql_wait_ready 60 # Wait up to 60 seconds
#
mysql_wait_ready() {
local timeout="${1:-30}"
local elapsed=0
info "Waiting for MySQL to be ready..."
while [ $elapsed -lt $timeout ]; do
if mysql_is_ready; then
ok "MySQL is ready"
return 0
fi
sleep 2
elapsed=$((elapsed + 2))
done
err "MySQL did not become ready within ${timeout}s"
return 1
}
# =============================================================================
# FILE & DIRECTORY HELPERS
# =============================================================================
# Ensure directory exists and is writable
# Creates directory if needed and sets permissions
#
# Usage:
# ensure_writable_dir /path/to/directory
#
ensure_writable_dir() {
local dir="$1"
if [ ! -d "$dir" ]; then
mkdir -p "$dir" 2>/dev/null || {
err "Failed to create directory: $dir"
return 1
}
fi
if [ ! -w "$dir" ]; then
chmod u+w "$dir" 2>/dev/null || {
err "Directory not writable: $dir"
return 1
}
fi
return 0
}
# Create backup of file before modification
#
# Usage:
# backup_file /path/to/important.conf
# # Creates /path/to/important.conf.backup.TIMESTAMP
#
backup_file() {
local file="$1"
if [ ! -f "$file" ]; then
warn "File does not exist, skipping backup: $file"
return 0
fi
local backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$file" "$backup" || {
err "Failed to create backup: $backup"
return 1
}
info "Created backup: $backup"
return 0
}
# =============================================================================
# GIT HELPERS
# =============================================================================
# Configure git identity if not already set
#
# Usage:
# setup_git_config [USERNAME] [EMAIL]
#
setup_git_config() {
local git_user="${1:-${GIT_USERNAME:-AzerothCore RealmMaster}}"
local git_email="${2:-${GIT_EMAIL:-noreply@azerothcore.org}}"
if ! git config --global user.name >/dev/null 2>&1; then
info "Configuring git identity: $git_user <$git_email>"
git config --global user.name "$git_user" || true
git config --global user.email "$git_email" || true
fi
}
# =============================================================================
# ERROR HANDLING UTILITIES
# =============================================================================
# Retry command with exponential backoff
#
# Usage:
# retry 5 docker pull myimage:latest
# retry 3 2 mysql_query "acore_auth" "SELECT 1" # 3 retries with 2s initial delay
#
retry() {
local max_attempts="$1"
shift
local delay="${1:-1}"
# Check if delay is a number, if not treat it as part of the command
if ! [[ "$delay" =~ ^[0-9]+$ ]]; then
delay=1
else
shift
fi
local attempt=1
local exit_code=0
while [ $attempt -le "$max_attempts" ]; do
if "$@"; then
return 0
fi
exit_code=$?
if [ $attempt -lt "$max_attempts" ]; then
warn "Command failed (attempt $attempt/$max_attempts), retrying in ${delay}s..."
sleep "$delay"
delay=$((delay * 2)) # Exponential backoff
fi
attempt=$((attempt + 1))
done
err "Command failed after $max_attempts attempts"
return $exit_code
}
# =============================================================================
# INITIALIZATION
# =============================================================================
# Library loaded successfully
# Scripts can check for $_COMMON_LIB_LOADED to verify library is loaded