einige Ergänzungen

This commit is contained in:
2025-07-25 23:16:16 +02:00
parent 4c382e64a5
commit 7e0b56a247
70 changed files with 7795 additions and 1894 deletions
+462
View File
@@ -0,0 +1,462 @@
#!/bin/bash
# =============================================================================
# Common Utilities Library for Meldestelle Shell Scripts
# =============================================================================
# This library provides common functions for logging, error handling, cleanup,
# and other utilities used across all shell scripts in the project.
#
# Usage: source "$(dirname "$0")/utils/common.sh" || source "scripts/utils/common.sh"
# =============================================================================
# Prevent multiple sourcing
if [[ "${COMMON_UTILS_LOADED:-}" == "true" ]]; then
return 0
fi
COMMON_UTILS_LOADED=true
# =============================================================================
# Configuration and Constants
# =============================================================================
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly WHITE='\033[1;37m'
readonly NC='\033[0m' # No Color
# Symbols
readonly CHECK_MARK="✓"
readonly CROSS_MARK="✗"
readonly WARNING_MARK="⚠"
readonly INFO_MARK=""
readonly ARROW_MARK="→"
# Global counters
ERRORS=0
WARNINGS=0
CHECKS=0
START_TIME=$(date +%s)
# =============================================================================
# Error Handling and Cleanup
# =============================================================================
# Enhanced error handling
set -euo pipefail
# Error trap function
error_trap() {
local exit_code=$?
local line_number=$1
log_error "Script failed at line $line_number with exit code $exit_code"
cleanup_on_exit
exit $exit_code
}
# Set error trap
trap 'error_trap $LINENO' ERR
# Cleanup function (can be overridden by scripts)
cleanup_on_exit() {
if declare -f cleanup > /dev/null; then
log_info "Running cleanup..."
cleanup
fi
}
# Set exit trap
trap cleanup_on_exit EXIT
# =============================================================================
# Logging Functions
# =============================================================================
# Get timestamp
get_timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
# Base logging function
log_base() {
local level=$1
local color=$2
local symbol=$3
local message=$4
local timestamp=$(get_timestamp)
echo -e "${color}[${timestamp}] ${symbol} [${level}]${NC} ${message}" >&2
}
# Info logging
log_info() {
log_base "INFO" "$BLUE" "$INFO_MARK" "$1"
}
# Success logging
log_success() {
log_base "SUCCESS" "$GREEN" "$CHECK_MARK" "$1"
}
# Warning logging
log_warning() {
log_base "WARNING" "$YELLOW" "$WARNING_MARK" "$1"
((WARNINGS++))
}
# Error logging
log_error() {
log_base "ERROR" "$RED" "$CROSS_MARK" "$1"
((ERRORS++))
}
# Debug logging (only if DEBUG=true)
log_debug() {
if [[ "${DEBUG:-false}" == "true" ]]; then
log_base "DEBUG" "$PURPLE" "🐛" "$1"
fi
}
# Progress logging
log_progress() {
log_base "PROGRESS" "$CYAN" "$ARROW_MARK" "$1"
}
# Section header
log_section() {
local title=$1
local line=$(printf '=%.0s' {1..80})
echo -e "\n${BLUE}${line}${NC}"
echo -e "${BLUE}${title}${NC}"
echo -e "${BLUE}${line}${NC}\n"
}
# =============================================================================
# Status and Validation Functions
# =============================================================================
# Print status with counter increment
print_status() {
local status=$1
local message=$2
((CHECKS++))
case $status in
"OK"|"SUCCESS")
log_success "$message"
;;
"WARNING"|"WARN")
log_warning "$message"
;;
"ERROR"|"FAIL")
log_error "$message"
;;
"INFO")
log_info "$message"
;;
*)
log_info "$message"
;;
esac
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if file exists with logging
check_file() {
local file=$1
local description=${2:-"File"}
if [[ -f "$file" ]]; then
print_status "OK" "$description exists: $file"
return 0
else
print_status "ERROR" "$description not found: $file"
return 1
fi
}
# Check if directory exists with logging
check_directory() {
local dir=$1
local description=${2:-"Directory"}
if [[ -d "$dir" ]]; then
print_status "OK" "$description exists: $dir"
return 0
else
print_status "ERROR" "$description not found: $dir"
return 1
fi
}
# Check if service is running on port
check_service_port() {
local port=$1
local service_name=${2:-"Service"}
local timeout=${3:-30}
log_info "Checking if $service_name is running on port $port..."
if timeout "$timeout" bash -c "until nc -z localhost $port; do sleep 1; done" 2>/dev/null; then
print_status "OK" "$service_name is running on port $port"
return 0
else
print_status "ERROR" "$service_name is not running on port $port (timeout: ${timeout}s)"
return 1
fi
}
# Check HTTP endpoint with retry
check_http_endpoint() {
local url=$1
local service_name=${2:-"Service"}
local timeout=${3:-30}
local retry_count=${4:-3}
log_info "Checking HTTP endpoint: $url"
for ((i=1; i<=retry_count; i++)); do
if timeout "$timeout" curl -sf "$url" >/dev/null 2>&1; then
print_status "OK" "$service_name endpoint is healthy: $url"
return 0
else
if [[ $i -lt $retry_count ]]; then
log_warning "Attempt $i/$retry_count failed, retrying in 5 seconds..."
sleep 5
fi
fi
done
print_status "ERROR" "$service_name endpoint is not healthy: $url (after $retry_count attempts)"
return 1
}
# =============================================================================
# Utility Functions
# =============================================================================
# Wait for service with timeout
wait_for_service() {
local check_command=$1
local service_name=$2
local timeout=${3:-60}
local interval=${4:-5}
log_info "Waiting for $service_name to be ready (timeout: ${timeout}s)..."
local elapsed=0
while [[ $elapsed -lt $timeout ]]; do
if eval "$check_command" >/dev/null 2>&1; then
log_success "$service_name is ready"
return 0
fi
sleep "$interval"
elapsed=$((elapsed + interval))
log_progress "Waiting for $service_name... (${elapsed}s/${timeout}s)"
done
log_error "$service_name failed to become ready within ${timeout}s"
return 1
}
# Create directory with logging
create_directory() {
local dir=$1
local description=${2:-"Directory"}
if [[ ! -d "$dir" ]]; then
if mkdir -p "$dir"; then
log_success "$description created: $dir"
else
log_error "Failed to create $description: $dir"
return 1
fi
else
log_info "$description already exists: $dir"
fi
}
# Backup file with timestamp
backup_file() {
local file=$1
local backup_dir=${2:-"./backups"}
if [[ -f "$file" ]]; then
create_directory "$backup_dir" "Backup directory"
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$backup_dir/$(basename "$file").backup.$timestamp"
if cp "$file" "$backup_file"; then
log_success "File backed up: $file$backup_file"
echo "$backup_file"
else
log_error "Failed to backup file: $file"
return 1
fi
else
log_warning "File not found for backup: $file"
return 1
fi
}
# Run command with timeout and logging
run_with_timeout() {
local timeout_duration=$1
local description=$2
shift 2
local command=("$@")
log_info "Running: $description"
log_debug "Command: ${command[*]}"
if timeout "$timeout_duration" "${command[@]}"; then
log_success "$description completed successfully"
return 0
else
local exit_code=$?
if [[ $exit_code -eq 124 ]]; then
log_error "$description timed out after ${timeout_duration}s"
else
log_error "$description failed with exit code $exit_code"
fi
return $exit_code
fi
}
# =============================================================================
# Summary and Reporting Functions
# =============================================================================
# Print execution summary
print_summary() {
local script_name=${1:-"Script"}
local end_time=$(date +%s)
local duration=$((end_time - START_TIME))
log_section "Execution Summary"
echo -e "Script: ${WHITE}$script_name${NC}"
echo -e "Duration: ${WHITE}${duration}s${NC}"
echo -e "Total checks: ${WHITE}$CHECKS${NC}"
echo -e "${GREEN}Successful: $((CHECKS - ERRORS - WARNINGS))${NC}"
echo -e "${YELLOW}Warnings: $WARNINGS${NC}"
echo -e "${RED}Errors: $ERRORS${NC}"
echo
if [[ $ERRORS -eq 0 ]]; then
if [[ $WARNINGS -eq 0 ]]; then
log_success "All checks passed! $script_name completed successfully."
return 0
else
log_warning "$script_name completed with warnings. Please review the warnings above."
return 0
fi
else
log_error "$script_name failed with $ERRORS errors. Please fix the errors above."
return 1
fi
}
# =============================================================================
# Environment and Configuration
# =============================================================================
# Load environment file if it exists
load_env_file() {
local env_file=${1:-.env}
if [[ -f "$env_file" ]]; then
log_info "Loading environment from: $env_file"
set -a
# shellcheck source=/dev/null
source "$env_file"
set +a
log_success "Environment loaded successfully"
else
log_warning "Environment file not found: $env_file"
fi
}
# Validate required environment variables
validate_env_vars() {
local vars=("$@")
local missing_vars=()
for var in "${vars[@]}"; do
if [[ -z "${!var:-}" ]]; then
missing_vars+=("$var")
fi
done
if [[ ${#missing_vars[@]} -gt 0 ]]; then
log_error "Missing required environment variables: ${missing_vars[*]}"
return 1
else
log_success "All required environment variables are set"
return 0
fi
}
# =============================================================================
# Docker and Service Management
# =============================================================================
# Check if Docker is running
check_docker() {
if command_exists docker && docker info >/dev/null 2>&1; then
print_status "OK" "Docker is running"
return 0
else
print_status "ERROR" "Docker is not running or not accessible"
return 1
fi
}
# Check if docker-compose is available
check_docker_compose() {
if command_exists docker-compose; then
print_status "OK" "docker-compose is available"
return 0
elif docker compose version >/dev/null 2>&1; then
print_status "OK" "docker compose (plugin) is available"
return 0
else
print_status "ERROR" "Neither docker-compose nor docker compose is available"
return 1
fi
}
# Start Docker services with health check wait
start_docker_services() {
local services=("$@")
local compose_file=${COMPOSE_FILE:-docker-compose.yml}
log_info "Starting Docker services: ${services[*]}"
if docker-compose -f "$compose_file" up -d "${services[@]}"; then
log_success "Docker services started"
# Wait for services to be healthy
for service in "${services[@]}"; do
wait_for_service "docker-compose -f $compose_file ps $service | grep -q 'healthy\\|Up'" "$service" 120 10
done
else
log_error "Failed to start Docker services"
return 1
fi
}
# =============================================================================
# Initialization
# =============================================================================
log_debug "Common utilities library loaded successfully"