#!/bin/bash # TSYS Developer Support Stack - Demo Deployment Script # Version: 1.0 # Purpose: Dynamic deployment with user detection and validation set -euo pipefail # Script Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DEMO_ENV_FILE="$PROJECT_ROOT/demo.env" COMPOSE_FILE="$PROJECT_ROOT/docker-compose.yml" # Color Codes for Output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging Functions log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # Function to detect current user and group IDs detect_user_ids() { log_info "Detecting user and group IDs..." local uid local gid local docker_gid uid=$(id -u) gid=$(id -g) docker_gid=$(getent group docker | cut -d: -f3) if [[ -z "$docker_gid" ]]; then log_error "Docker group not found. Please ensure Docker is installed and user is in docker group." exit 1 fi log_info "Detected UID: $uid, GID: $gid, Docker GID: $docker_gid" # Update demo.env with detected values sed -i "s/^DEMO_UID=$/DEMO_UID=$uid/" "$DEMO_ENV_FILE" sed -i "s/^DEMO_GID=$/DEMO_GID=$gid/" "$DEMO_ENV_FILE" sed -i "s/^DEMO_DOCKER_GID=$/DEMO_DOCKER_GID=$docker_gid/" "$DEMO_ENV_FILE" log_success "User IDs detected and configured" } # Function to validate prerequisites validate_prerequisites() { log_info "Validating prerequisites..." # Check if Docker is installed and running if ! command -v docker &> /dev/null; then log_error "Docker is not installed or not in PATH" exit 1 fi if ! docker info &> /dev/null; then log_error "Docker daemon is not running" exit 1 fi # Check if Docker Compose is available if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then log_error "Docker Compose is not installed" exit 1 fi # Check if demo.env exists if [[ ! -f "$DEMO_ENV_FILE" ]]; then log_error "demo.env file not found at $DEMO_ENV_FILE" exit 1 fi log_success "Prerequisites validation passed" } # Function to generate docker-compose.yml from template generate_compose_file() { log_info "Generating docker-compose.yml..." # Check if template exists (will be created in next phase) local template_file="$PROJECT_ROOT/docker-compose.yml.template" if [[ ! -f "$template_file" ]]; then log_error "Docker Compose template not found at $template_file" log_info "Please ensure the template file is created before running deployment" exit 1 fi # Source and export environment variables # shellcheck disable=SC1090,SC1091 set -a source "$DEMO_ENV_FILE" set +a # Generate docker-compose.yml from template envsubst < "$template_file" > "$COMPOSE_FILE" log_success "docker-compose.yml generated successfully" } # Function to deploy the stack deploy_stack() { log_info "Deploying TSYS Developer Support Stack..." # Change to project directory cd "$PROJECT_ROOT" # Deploy the stack if command -v docker-compose &> /dev/null; then docker-compose -f "$COMPOSE_FILE" up -d else docker compose -f "$COMPOSE_FILE" up -d fi log_success "Stack deployment initiated" } # Function to wait for services to be healthy wait_for_services() { log_info "Waiting for services to become healthy..." local max_wait=300 # 5 minutes local wait_interval=10 local elapsed=0 while [[ $elapsed -lt $max_wait ]]; do local unhealthy_services=0 # Check service health (will be implemented with actual service names) if command -v docker-compose &> /dev/null; then mapfile -t services < <(docker-compose -f "$COMPOSE_FILE" config --services) else mapfile -t services < <(docker compose -f "$COMPOSE_FILE" config --services) fi for service in "${services[@]}"; do local health_status if command -v docker-compose &> /dev/null; then health_status=$(docker-compose -f "$COMPOSE_FILE" ps -q "$service" | xargs docker inspect --format='{{.State.Health.Status}}' 2>/dev/null || echo "none") else health_status=$(docker compose -f "$COMPOSE_FILE" ps -q "$service" | xargs docker inspect --format='{{.State.Health.Status}}' 2>/dev/null || echo "none") fi if [[ "$health_status" != "healthy" && "$health_status" != "none" ]]; then ((unhealthy_services++)) fi done if [[ $unhealthy_services -eq 0 ]]; then log_success "All services are healthy" return 0 fi log_info "$unhealthy_services services still unhealthy... waiting ${wait_interval}s" sleep $wait_interval elapsed=$((elapsed + wait_interval)) done log_warning "Timeout reached. Some services may not be fully healthy." return 1 } # Function to display deployment summary display_summary() { log_success "TSYS Developer Support Stack Deployment Summary" echo "==================================================" echo "📊 Homepage Dashboard: http://localhost:${HOMEPAGE_PORT:-4000}" echo "🏗️ Infrastructure Services:" echo " - Pi-hole (DNS): http://localhost:${PIHOLE_PORT:-4006}" echo " - Dockhand (Containers): http://localhost:${DOCKHAND_PORT:-4007}" echo "📊 Monitoring & Observability:" echo " - InfluxDB (Database): http://localhost:${INFLUXDB_PORT:-4008}" echo " - Grafana (Visualization): http://localhost:${GRAFANA_PORT:-4009}" echo "📚 Documentation & Diagramming:" echo " - Draw.io (Diagrams): http://localhost:${DRAWIO_PORT:-4010}" echo " - Kroki (Diagrams as Service): http://localhost:${KROKI_PORT:-4011}" echo "🛠️ Developer Tools:" echo " - Atomic Tracker (Habits): http://localhost:${ATOMIC_TRACKER_PORT:-4012}" echo " - ArchiveBox (Archiving): http://localhost:${ARCHIVEBOX_PORT:-4013}" echo " - Tube Archivist (YouTube): http://localhost:${TUBE_ARCHIVIST_PORT:-4014}" echo " - Wakapi (Time Tracking): http://localhost:${WAKAPI_PORT:-4015}" echo " - MailHog (Email Testing): http://localhost:${MAILHOG_PORT:-4017}" echo " - Atuin (Shell History): http://localhost:${ATUIN_PORT:-4018}" echo "==================================================" echo "🔐 Demo Credentials:" echo " Username: ${DEMO_ADMIN_USER:-admin}" echo " Password: ${DEMO_ADMIN_PASSWORD:-demo_password}" echo "⚠️ FOR DEMONSTRATION PURPOSES ONLY - NOT FOR PRODUCTION" } # Function to stop the stack stop_stack() { log_info "Stopping TSYS Developer Support Stack..." cd "$PROJECT_ROOT" if command -v docker-compose &> /dev/null; then docker-compose -f "$COMPOSE_FILE" down else docker compose -f "$COMPOSE_FILE" down fi log_success "Stack stopped" } # Function to restart the stack restart_stack() { log_info "Restarting TSYS Developer Support Stack..." stop_stack sleep 5 deploy_stack wait_for_services display_summary } # Function to show usage show_usage() { echo "Usage: $0 {deploy|stop|restart|status|help}" echo "" echo "Commands:" echo " deploy - Deploy the complete stack" echo " stop - Stop all services" echo " restart - Restart all services" echo " status - Show service status" echo " help - Show this help message" } # Function to show status show_status() { log_info "TSYS Developer Support Stack Status" echo "====================================" cd "$PROJECT_ROOT" if command -v docker-compose &> /dev/null; then docker-compose -f "$COMPOSE_FILE" ps else docker compose -f "$COMPOSE_FILE" ps fi } # Main script execution main() { case "${1:-deploy}" in deploy) validate_prerequisites detect_user_ids generate_compose_file deploy_stack wait_for_services display_summary ;; stop) stop_stack ;; restart) restart_stack ;; status) show_status ;; help|--help|-h) show_usage ;; *) log_error "Unknown command: $1" show_usage exit 1 ;; esac } # Execute main function with all arguments main "$@"