#!/bin/bash # Portfolio Deployment Script # Usage: ./scripts/deploy.sh [environment] set -e # Configuration ENVIRONMENT=${1:-production} REGISTRY="ghcr.io" IMAGE_NAME="dennis-konkol/my_portfolio" CONTAINER_NAME="portfolio-app" COMPOSE_FILE="docker-compose.prod.yml" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging function log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } # Check if running as root if [[ $EUID -eq 0 ]]; then error "This script should not be run as root" exit 1 fi # Check if Docker is running if ! docker info > /dev/null 2>&1; then error "Docker is not running. Please start Docker and try again." exit 1 fi # Check if docker-compose is available if ! command -v docker-compose &> /dev/null; then error "docker-compose is not installed. Please install docker-compose and try again." exit 1 fi # Check if .env file exists if [ ! -f .env ]; then error ".env file not found. Please create it with the required environment variables." exit 1 fi log "Starting deployment for environment: $ENVIRONMENT" # Set image tag based on environment if [ "$ENVIRONMENT" = "production" ]; then IMAGE_TAG="production" else IMAGE_TAG="main" fi FULL_IMAGE_NAME="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" log "Using image: $FULL_IMAGE_NAME" # Login to registry (if needed) log "Logging in to container registry..." echo "$GITHUB_TOKEN" | docker login $REGISTRY -u $GITHUB_ACTOR --password-stdin || { warning "Failed to login to registry. Make sure GITHUB_TOKEN and GITHUB_ACTOR are set." } # Pull latest image log "Pulling latest image..." docker pull $FULL_IMAGE_NAME || { error "Failed to pull image $FULL_IMAGE_NAME" exit 1 } # Stop and remove old containers log "Stopping old containers..." docker-compose -f $COMPOSE_FILE down || { warning "No old containers to stop" } # Remove old images (keep last 3 versions) log "Cleaning up old images..." docker images $REGISTRY/$IMAGE_NAME --format "table {{.Tag}}\t{{.ID}}" | tail -n +2 | head -n -3 | awk '{print $2}' | xargs -r docker rmi || { warning "No old images to remove" } # Start new containers log "Starting new containers..." docker-compose -f $COMPOSE_FILE up -d || { error "Failed to start containers" exit 1 } # Wait for health check log "Waiting for application to be healthy..." HEALTH_CHECK_TIMEOUT=60 HEALTH_CHECK_INTERVAL=2 ELAPSED=0 while [ $ELAPSED -lt $HEALTH_CHECK_TIMEOUT ]; do if curl -f http://localhost:3000/api/health > /dev/null 2>&1; then success "Application is healthy!" break fi sleep $HEALTH_CHECK_INTERVAL ELAPSED=$((ELAPSED + HEALTH_CHECK_INTERVAL)) echo -n "." done if [ $ELAPSED -ge $HEALTH_CHECK_TIMEOUT ]; then error "Health check timeout. Application may not be running properly." log "Container logs:" docker-compose -f $COMPOSE_FILE logs --tail=50 exit 1 fi # Verify deployment log "Verifying deployment..." if curl -f http://localhost:3000/api/health > /dev/null 2>&1; then success "Deployment successful!" # Show container status log "Container status:" docker-compose -f $COMPOSE_FILE ps # Show resource usage log "Resource usage:" docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" else error "Deployment verification failed!" log "Container logs:" docker-compose -f $COMPOSE_FILE logs --tail=50 exit 1 fi # Cleanup log "Cleaning up unused Docker resources..." docker system prune -f --volumes || { warning "Failed to clean up Docker resources" } success "Deployment completed successfully!" log "Application is available at: http://localhost:3000" log "Health check endpoint: http://localhost:3000/api/health"