Resolved multiple critical discovery system issues: ## Discovery System Fixes - Fixed console commands not being discovered on first run - Implemented fallback discovery for empty caches - Added context-aware caching with separate cache keys - Fixed object serialization preventing __PHP_Incomplete_Class ## Cache System Improvements - Smart caching that only caches meaningful results - Separate caches for different execution contexts (console, web, test) - Proper array serialization/deserialization for cache compatibility - Cache hit logging for debugging and monitoring ## Object Serialization Fixes - Fixed DiscoveredAttribute serialization with proper string conversion - Sanitized additional data to prevent object reference issues - Added fallback for corrupted cache entries ## Performance & Reliability - All 69 console commands properly discovered and cached - 534 total discovery items successfully cached and restored - No more __PHP_Incomplete_Class cache corruption - Improved error handling and graceful fallbacks ## Testing & Quality - Fixed code style issues across discovery components - Enhanced logging for better debugging capabilities - Improved cache validation and error recovery Ready for production deployment with stable discovery system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
628 lines
20 KiB
Bash
Executable File
628 lines
20 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# First-time Setup Script for Custom PHP Framework Deployment
|
|
# Domain: michaelschiemer.de | Email: kontakt@michaelschiemer.de | PHP: 8.4
|
|
# Usage: ./setup.sh [options]
|
|
|
|
set -euo pipefail
|
|
|
|
# Script configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../" && pwd)"
|
|
DEPLOYMENT_DIR="${SCRIPT_DIR}"
|
|
|
|
# Default configuration
|
|
INSTALL_DEPENDENCIES=true
|
|
SETUP_CONFIGS=true
|
|
GENERATE_KEYS=true
|
|
VERBOSE=false
|
|
SKIP_PROMPTS=false
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
WHITE='\033[1;37m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log() {
|
|
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ✅ INFO: $1${NC}"
|
|
}
|
|
|
|
warn() {
|
|
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] ⚠️ WARN: $1${NC}"
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ❌ ERROR: $1${NC}"
|
|
}
|
|
|
|
debug() {
|
|
if [ "$VERBOSE" = true ]; then
|
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] 🔍 DEBUG: $1${NC}"
|
|
fi
|
|
}
|
|
|
|
success() {
|
|
echo -e "${WHITE}[$(date +'%Y-%m-%d %H:%M:%S')] 🎉 SUCCESS: $1${NC}"
|
|
}
|
|
|
|
section() {
|
|
echo -e "\n${PURPLE}================================${NC}"
|
|
echo -e "${PURPLE}$1${NC}"
|
|
echo -e "${PURPLE}================================${NC}\n"
|
|
}
|
|
|
|
# Usage information
|
|
show_usage() {
|
|
cat << EOF
|
|
${WHITE}Custom PHP Framework Deployment Setup${NC}
|
|
${CYAN}Domain: michaelschiemer.de | Email: kontakt@michaelschiemer.de | PHP: 8.4${NC}
|
|
|
|
${WHITE}Usage:${NC} $0 [options]
|
|
|
|
${WHITE}Options:${NC}
|
|
${YELLOW}--skip-dependencies${NC} Skip dependency installation
|
|
${YELLOW}--skip-configs${NC} Skip configuration setup
|
|
${YELLOW}--skip-keys${NC} Skip SSH key generation
|
|
${YELLOW}--skip-prompts${NC} Skip interactive prompts (use defaults)
|
|
${YELLOW}--verbose${NC} Enable verbose output
|
|
${YELLOW}-h, --help${NC} Show this help message
|
|
|
|
${WHITE}What this script does:${NC}
|
|
• Installs required dependencies (Docker, Ansible, etc.)
|
|
• Sets up configuration files from templates
|
|
• Generates SSH keys for deployment
|
|
• Validates the deployment environment
|
|
• Prepares the system for first deployment
|
|
|
|
${WHITE}Examples:${NC}
|
|
${CYAN}$0${NC} # Full setup with prompts
|
|
${CYAN}$0 --skip-prompts${NC} # Automated setup
|
|
${CYAN}$0 --skip-dependencies --verbose${NC} # Setup configs only with debug output
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
parse_arguments() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--skip-dependencies)
|
|
INSTALL_DEPENDENCIES=false
|
|
shift
|
|
;;
|
|
--skip-configs)
|
|
SETUP_CONFIGS=false
|
|
shift
|
|
;;
|
|
--skip-keys)
|
|
GENERATE_KEYS=false
|
|
shift
|
|
;;
|
|
--skip-prompts)
|
|
SKIP_PROMPTS=true
|
|
shift
|
|
;;
|
|
--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
error "Unknown argument: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Detect operating system
|
|
detect_os() {
|
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
OS="ubuntu"
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
OS="centos"
|
|
else
|
|
OS="linux"
|
|
fi
|
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
|
OS="macos"
|
|
else
|
|
OS="unknown"
|
|
fi
|
|
|
|
debug "Detected OS: $OS"
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [[ $EUID -eq 0 ]]; then
|
|
warn "Running as root. Some operations may require different permissions."
|
|
fi
|
|
}
|
|
|
|
# Install dependencies based on OS
|
|
install_dependencies() {
|
|
if [ "$INSTALL_DEPENDENCIES" = false ]; then
|
|
debug "Skipping dependency installation"
|
|
return 0
|
|
fi
|
|
|
|
section "INSTALLING DEPENDENCIES"
|
|
|
|
log "Installing required dependencies for $OS"
|
|
|
|
case $OS in
|
|
ubuntu)
|
|
log "Updating package lists"
|
|
sudo apt-get update
|
|
|
|
log "Installing required packages"
|
|
sudo apt-get install -y \
|
|
curl \
|
|
wget \
|
|
git \
|
|
python3 \
|
|
python3-pip \
|
|
software-properties-common \
|
|
apt-transport-https \
|
|
ca-certificates \
|
|
gnupg \
|
|
lsb-release
|
|
|
|
# Install Docker
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
log "Installing Docker"
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
|
|
# Add user to docker group
|
|
sudo usermod -aG docker "$USER"
|
|
warn "You need to log out and back in for Docker permissions to take effect"
|
|
else
|
|
debug "Docker already installed"
|
|
fi
|
|
|
|
# Install Docker Compose (standalone)
|
|
if ! command -v docker-compose >/dev/null 2>&1; then
|
|
log "Installing Docker Compose"
|
|
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
sudo chmod +x /usr/local/bin/docker-compose
|
|
else
|
|
debug "Docker Compose already installed"
|
|
fi
|
|
|
|
# Install Ansible
|
|
if ! command -v ansible-playbook >/dev/null 2>&1; then
|
|
log "Installing Ansible"
|
|
sudo apt-get install -y ansible
|
|
else
|
|
debug "Ansible already installed"
|
|
fi
|
|
;;
|
|
|
|
centos)
|
|
log "Installing required packages for CentOS/RHEL"
|
|
sudo yum update -y
|
|
sudo yum install -y curl wget git python3 python3-pip
|
|
|
|
# Install Docker
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
log "Installing Docker"
|
|
sudo yum install -y yum-utils
|
|
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
|
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
sudo systemctl start docker
|
|
sudo systemctl enable docker
|
|
sudo usermod -aG docker "$USER"
|
|
else
|
|
debug "Docker already installed"
|
|
fi
|
|
|
|
# Install Ansible
|
|
if ! command -v ansible-playbook >/dev/null 2>&1; then
|
|
log "Installing Ansible"
|
|
sudo yum install -y epel-release
|
|
sudo yum install -y ansible
|
|
else
|
|
debug "Ansible already installed"
|
|
fi
|
|
;;
|
|
|
|
macos)
|
|
if ! command -v brew >/dev/null 2>&1; then
|
|
error "Homebrew not found. Please install Homebrew first:"
|
|
error "https://brew.sh/"
|
|
exit 1
|
|
fi
|
|
|
|
log "Installing required packages for macOS"
|
|
|
|
# Install Docker Desktop
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
log "Installing Docker Desktop"
|
|
brew install --cask docker
|
|
warn "Please start Docker Desktop application manually"
|
|
else
|
|
debug "Docker already installed"
|
|
fi
|
|
|
|
# Install Docker Compose
|
|
if ! command -v docker-compose >/dev/null 2>&1; then
|
|
log "Installing Docker Compose"
|
|
brew install docker-compose
|
|
else
|
|
debug "Docker Compose already installed"
|
|
fi
|
|
|
|
# Install Ansible
|
|
if ! command -v ansible-playbook >/dev/null 2>&1; then
|
|
log "Installing Ansible"
|
|
brew install ansible
|
|
else
|
|
debug "Ansible already installed"
|
|
fi
|
|
;;
|
|
|
|
*)
|
|
error "Unsupported operating system: $OS"
|
|
error "Please install dependencies manually:"
|
|
error "- Docker (docker.com)"
|
|
error "- Docker Compose"
|
|
error "- Ansible (ansible.com)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
success "Dependencies installation completed"
|
|
}
|
|
|
|
# Verify installations
|
|
verify_dependencies() {
|
|
section "VERIFYING DEPENDENCIES"
|
|
|
|
local all_good=true
|
|
|
|
# Check Docker
|
|
if command -v docker >/dev/null 2>&1; then
|
|
local docker_version=$(docker --version)
|
|
log "✓ Docker: $docker_version"
|
|
|
|
# Test Docker without sudo
|
|
if docker ps >/dev/null 2>&1; then
|
|
debug "✓ Docker permissions configured correctly"
|
|
else
|
|
warn "Docker requires sudo. You may need to log out and back in."
|
|
fi
|
|
else
|
|
error "❌ Docker not found"
|
|
all_good=false
|
|
fi
|
|
|
|
# Check Docker Compose
|
|
if command -v docker-compose >/dev/null 2>&1; then
|
|
local compose_version=$(docker-compose --version)
|
|
log "✓ Docker Compose: $compose_version"
|
|
else
|
|
error "❌ Docker Compose not found"
|
|
all_good=false
|
|
fi
|
|
|
|
# Check Ansible
|
|
if command -v ansible-playbook >/dev/null 2>&1; then
|
|
local ansible_version=$(ansible --version | head -n1)
|
|
log "✓ Ansible: $ansible_version"
|
|
else
|
|
error "❌ Ansible not found"
|
|
all_good=false
|
|
fi
|
|
|
|
# Check Python
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
local python_version=$(python3 --version)
|
|
log "✓ Python: $python_version"
|
|
else
|
|
warn "Python3 not found (may be required for some Ansible modules)"
|
|
fi
|
|
|
|
if [ "$all_good" = false ]; then
|
|
error "Some dependencies are missing. Please install them and run setup again."
|
|
exit 1
|
|
fi
|
|
|
|
success "All dependencies verified"
|
|
}
|
|
|
|
# Setup configuration files
|
|
setup_configuration() {
|
|
if [ "$SETUP_CONFIGS" = false ]; then
|
|
debug "Skipping configuration setup"
|
|
return 0
|
|
fi
|
|
|
|
section "SETTING UP CONFIGURATION"
|
|
|
|
# Create environment files from templates
|
|
log "Creating environment configuration files"
|
|
|
|
local environments=("development" "staging" "production")
|
|
|
|
for env in "${environments[@]}"; do
|
|
local env_file="${DEPLOYMENT_DIR}/applications/environments/.env.${env}"
|
|
local template_file="${env_file}.template"
|
|
|
|
if [[ -f "$template_file" ]]; then
|
|
if [[ ! -f "$env_file" ]]; then
|
|
log "Creating .env.${env} from template"
|
|
cp "$template_file" "$env_file"
|
|
|
|
# Basic substitutions
|
|
if [ "$SKIP_PROMPTS" = false ]; then
|
|
echo -e "${CYAN}Configuring $env environment:${NC}"
|
|
|
|
# Domain configuration
|
|
if [ "$env" = "production" ]; then
|
|
sed -i 's/your-domain\.com/michaelschiemer.de/g' "$env_file"
|
|
sed -i 's/your-email@example\.com/kontakt@michaelschiemer.de/g' "$env_file"
|
|
else
|
|
read -p "Domain for $env (default: ${env}.michaelschiemer.de): " domain
|
|
domain=${domain:-"${env}.michaelschiemer.de"}
|
|
sed -i "s/your-domain\.com/$domain/g" "$env_file"
|
|
sed -i 's/your-email@example\.com/kontakt@michaelschiemer.de/g' "$env_file"
|
|
fi
|
|
|
|
# Generate random passwords
|
|
if command -v openssl >/dev/null 2>&1; then
|
|
local db_password=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
|
|
local app_key=$(openssl rand -base64 32)
|
|
|
|
sed -i "s/changeme/$db_password/g" "$env_file"
|
|
sed -i "s/*** REQUIRED - Generate random key ***/$app_key/g" "$env_file"
|
|
|
|
debug "Generated secure passwords for $env"
|
|
fi
|
|
else
|
|
# Non-interactive: use defaults
|
|
if [ "$env" = "production" ]; then
|
|
sed -i 's/your-domain\.com/michaelschiemer.de/g' "$env_file"
|
|
else
|
|
sed -i "s/your-domain\.com/${env}.michaelschiemer.de/g" "$env_file"
|
|
fi
|
|
sed -i 's/your-email@example\.com/kontakt@michaelschiemer.de/g' "$env_file"
|
|
|
|
warn "Using default configuration for $env. Review and update manually."
|
|
fi
|
|
else
|
|
debug ".env.${env} already exists"
|
|
fi
|
|
else
|
|
warn "Template not found: $template_file"
|
|
fi
|
|
done
|
|
|
|
# Set proper permissions
|
|
log "Setting configuration file permissions"
|
|
find "${DEPLOYMENT_DIR}/applications/environments" -name ".env.*" -type f -exec chmod 600 {} \;
|
|
|
|
# Create necessary directories
|
|
log "Creating necessary directories"
|
|
mkdir -p "${PROJECT_ROOT}/storage/backups"
|
|
mkdir -p "${PROJECT_ROOT}/storage/logs"
|
|
mkdir -p "${DEPLOYMENT_DIR}/infrastructure/logs"
|
|
|
|
success "Configuration setup completed"
|
|
}
|
|
|
|
# Generate SSH keys for deployment
|
|
generate_ssh_keys() {
|
|
if [ "$GENERATE_KEYS" = false ]; then
|
|
debug "Skipping SSH key generation"
|
|
return 0
|
|
fi
|
|
|
|
section "SETTING UP SSH KEYS"
|
|
|
|
local ssh_dir="$HOME/.ssh"
|
|
local key_name="michaelschiemer_deploy"
|
|
local private_key="${ssh_dir}/${key_name}"
|
|
local public_key="${private_key}.pub"
|
|
|
|
if [[ ! -f "$private_key" ]]; then
|
|
log "Generating SSH key pair for deployment"
|
|
|
|
mkdir -p "$ssh_dir"
|
|
chmod 700 "$ssh_dir"
|
|
|
|
ssh-keygen -t ed25519 -C "deployment@michaelschiemer.de" -f "$private_key" -N ""
|
|
|
|
log "SSH key pair generated:"
|
|
log "Private key: $private_key"
|
|
log "Public key: $public_key"
|
|
|
|
echo -e "\n${YELLOW}📋 IMPORTANT: Add the following public key to your deployment servers:${NC}\n"
|
|
cat "$public_key"
|
|
echo -e "\n${YELLOW}Copy this key to ~/.ssh/authorized_keys on your deployment servers${NC}"
|
|
|
|
if [ "$SKIP_PROMPTS" = false ]; then
|
|
echo
|
|
read -p "Press Enter when you have added the public key to your servers..."
|
|
fi
|
|
else
|
|
debug "SSH key already exists: $private_key"
|
|
log "Using existing SSH key: $private_key"
|
|
fi
|
|
|
|
# Add to SSH agent if running
|
|
if pgrep -x "ssh-agent" > /dev/null; then
|
|
log "Adding SSH key to agent"
|
|
ssh-add "$private_key" 2>/dev/null || debug "Key may already be in agent"
|
|
fi
|
|
|
|
success "SSH key setup completed"
|
|
}
|
|
|
|
# Test deployment environment
|
|
test_environment() {
|
|
section "TESTING DEPLOYMENT ENVIRONMENT"
|
|
|
|
log "Running environment validation tests"
|
|
|
|
# Test Docker
|
|
log "Testing Docker functionality"
|
|
if docker run --rm hello-world >/dev/null 2>&1; then
|
|
log "✓ Docker test passed"
|
|
else
|
|
warn "Docker test failed - you may need to start Docker or check permissions"
|
|
fi
|
|
|
|
# Test Docker Compose
|
|
log "Testing Docker Compose functionality"
|
|
cd "$PROJECT_ROOT"
|
|
if docker-compose config >/dev/null 2>&1; then
|
|
log "✓ Docker Compose configuration valid"
|
|
else
|
|
warn "Docker Compose configuration test failed"
|
|
fi
|
|
|
|
# Test Ansible
|
|
log "Testing Ansible functionality"
|
|
if ansible --version >/dev/null 2>&1; then
|
|
log "✓ Ansible test passed"
|
|
else
|
|
warn "Ansible test failed"
|
|
fi
|
|
|
|
# Test project structure
|
|
log "Validating project structure"
|
|
local required_files=(
|
|
"docker-compose.yml"
|
|
"deployment/deploy.sh"
|
|
"deployment/Makefile"
|
|
"deployment/applications/docker-compose.production.yml"
|
|
"deployment/infrastructure/site.yml"
|
|
)
|
|
|
|
local missing_files=()
|
|
for file in "${required_files[@]}"; do
|
|
if [[ ! -f "${PROJECT_ROOT}/${file}" ]]; then
|
|
missing_files+=("$file")
|
|
fi
|
|
done
|
|
|
|
if [ ${#missing_files[@]} -eq 0 ]; then
|
|
log "✓ All required project files found"
|
|
else
|
|
warn "Missing files:"
|
|
for file in "${missing_files[@]}"; do
|
|
warn " - $file"
|
|
done
|
|
fi
|
|
|
|
success "Environment testing completed"
|
|
}
|
|
|
|
# Show setup summary
|
|
show_setup_summary() {
|
|
section "SETUP SUMMARY"
|
|
|
|
cat << EOF
|
|
${WHITE}🎉 Custom PHP Framework Deployment Setup Complete! 🎉${NC}
|
|
|
|
${CYAN}What was configured:${NC}
|
|
EOF
|
|
|
|
if [ "$INSTALL_DEPENDENCIES" = true ]; then
|
|
echo "• ✅ Dependencies installed (Docker, Ansible, etc.)"
|
|
fi
|
|
|
|
if [ "$SETUP_CONFIGS" = true ]; then
|
|
echo "• ✅ Configuration files created from templates"
|
|
fi
|
|
|
|
if [ "$GENERATE_KEYS" = true ]; then
|
|
echo "• ✅ SSH keys generated for deployment"
|
|
fi
|
|
|
|
cat << EOF
|
|
|
|
${CYAN}Next Steps:${NC}
|
|
1. Review and customize environment configurations:
|
|
${YELLOW}• deployment/applications/environments/.env.development${NC}
|
|
${YELLOW}• deployment/applications/environments/.env.staging${NC}
|
|
${YELLOW}• deployment/applications/environments/.env.production${NC}
|
|
|
|
2. Configure your deployment servers:
|
|
${YELLOW}• Add your SSH public key to authorized_keys${NC}
|
|
${YELLOW}• Update Ansible inventory files if needed${NC}
|
|
|
|
3. Test your deployment:
|
|
${YELLOW}make deploy-dry ENV=development${NC}
|
|
|
|
4. Deploy to staging when ready:
|
|
${YELLOW}make deploy-staging${NC}
|
|
|
|
${CYAN}Useful Commands:${NC}
|
|
• ${YELLOW}make help${NC} # Show all available commands
|
|
• ${YELLOW}make status ENV=staging${NC} # Check deployment status
|
|
• ${YELLOW}make deploy-dry ENV=production${NC} # Test production deployment
|
|
• ${YELLOW}make info${NC} # Show deployment information
|
|
|
|
${GREEN}🌟 Your Custom PHP Framework deployment system is ready!${NC}
|
|
${CYAN}Domain: michaelschiemer.de | Email: kontakt@michaelschiemer.de | PHP: 8.4${NC}
|
|
|
|
EOF
|
|
}
|
|
|
|
# Error handling
|
|
cleanup() {
|
|
local exit_code=$?
|
|
|
|
if [ $exit_code -ne 0 ]; then
|
|
error "Setup failed with exit code: $exit_code"
|
|
echo
|
|
echo -e "${RED}Troubleshooting Tips:${NC}"
|
|
echo "• Check the error messages above for specific issues"
|
|
echo "• Ensure you have sufficient permissions"
|
|
echo "• Try running individual setup steps manually"
|
|
echo "• Check the deployment documentation"
|
|
echo "• Use --verbose flag for more detailed output"
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
# Main setup function
|
|
main() {
|
|
log "Starting Custom PHP Framework deployment setup"
|
|
|
|
detect_os
|
|
check_root
|
|
|
|
# Setup steps
|
|
install_dependencies
|
|
verify_dependencies
|
|
setup_configuration
|
|
generate_ssh_keys
|
|
test_environment
|
|
|
|
# Summary
|
|
show_setup_summary
|
|
|
|
success "Deployment setup completed successfully!"
|
|
}
|
|
|
|
# Script execution
|
|
parse_arguments "$@"
|
|
main |