#!/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