BCards/scripts/deploy-release.sh
Ricardo Carneiro 3d2ce1f8cf
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 4s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m22s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m54s
BCards Deployment Pipeline / Deploy to Test (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s
fix: Increase session timeout to 7 days and set SameSite=None for Cloudflare compatibility
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-18 12:32:42 -03:00

369 lines
11 KiB
Bash

#!/bin/bash
# Deploy script for Release environment with multi-architecture support
# Usage: ./deploy-release.sh <IMAGE_TAG>
set -euo pipefail
# Configuration
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly DEPLOY_DIR="/opt/bcards-staging"
readonly DOCKER_COMPOSE_FILE="docker-compose.staging.yml"
readonly CONTAINER_NAME="bcards-staging"
readonly HEALTH_CHECK_URL="http://localhost:8090/health"
readonly MAX_HEALTH_CHECK_ATTEMPTS=10
readonly HEALTH_CHECK_INTERVAL=10
readonly ROLLBACK_TIMEOUT=300
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly 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"
}
# Cleanup function
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
log_error "Deployment failed with exit code $exit_code"
rollback_deployment
fi
exit $exit_code
}
# Set trap for cleanup on exit
trap cleanup EXIT
# Validate input parameters
validate_input() {
if [ $# -ne 1 ]; then
log_error "Usage: $0 <IMAGE_TAG>"
exit 1
fi
local image_tag="$1"
if [[ ! "$image_tag" =~ ^[a-zA-Z0-9._-]+$ ]]; then
log_error "Invalid image tag format: $image_tag"
exit 1
fi
}
# Check prerequisites
check_prerequisites() {
log_info "Checking prerequisites..."
# Check if Docker is running
if ! docker info >/dev/null 2>&1; then
log_error "Docker is not running or not accessible"
exit 1
fi
# Check if docker-compose is available
if ! command -v docker-compose >/dev/null 2>&1; then
log_error "docker-compose is not installed"
exit 1
fi
# Check if deployment directory exists
if [ ! -d "$DEPLOY_DIR" ]; then
log_info "Creating deployment directory: $DEPLOY_DIR"
mkdir -p "$DEPLOY_DIR"
fi
log_success "Prerequisites check passed"
}
# Backup current deployment
backup_current_deployment() {
log_info "Backing up current deployment..."
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_dir="$DEPLOY_DIR/backups/$timestamp"
mkdir -p "$backup_dir"
# Backup environment file if exists
if [ -f "$DEPLOY_DIR/.env" ]; then
cp "$DEPLOY_DIR/.env" "$backup_dir/.env.backup"
cp "$DEPLOY_DIR/.env" "$DEPLOY_DIR/.env.backup"
log_info "Environment file backed up"
fi
# Backup docker-compose file if exists
if [ -f "$DEPLOY_DIR/$DOCKER_COMPOSE_FILE" ]; then
cp "$DEPLOY_DIR/$DOCKER_COMPOSE_FILE" "$backup_dir/${DOCKER_COMPOSE_FILE}.backup"
log_info "Docker compose file backed up"
fi
# Get current container image for potential rollback
if docker ps --format "table {{.Names}}\t{{.Image}}" | grep -q "$CONTAINER_NAME"; then
local current_image=$(docker inspect --format='{{.Config.Image}}' "$CONTAINER_NAME" 2>/dev/null || echo "")
if [ -n "$current_image" ]; then
echo "$current_image" > "$DEPLOY_DIR/.previous_image"
log_info "Current image backed up: $current_image"
fi
fi
log_success "Backup completed: $backup_dir"
}
# Test MongoDB connectivity
test_mongodb_connection() {
log_info "Testing MongoDB connectivity..."
local mongodb_host="192.168.0.100"
local mongodb_port="27017"
# Test basic connectivity
if timeout 10 bash -c "</dev/tcp/$mongodb_host/$mongodb_port" 2>/dev/null; then
log_success "MongoDB connection test passed"
else
log_error "Cannot connect to MongoDB at $mongodb_host:$mongodb_port"
return 1
fi
# Run detailed MongoDB test script if available
if [ -f "$SCRIPT_DIR/test-mongodb-connection.sh" ]; then
log_info "Running detailed MongoDB connection tests..."
bash "$SCRIPT_DIR/test-mongodb-connection.sh"
fi
}
# Pull new Docker image
pull_docker_image() {
local image_tag="$1"
local full_image="registry.redecarneir.us/bcards:$image_tag"
log_info "Pulling Docker image: $full_image"
# Pull the multi-arch image
if docker pull "$full_image"; then
log_success "Image pulled successfully"
else
log_error "Failed to pull image: $full_image"
return 1
fi
# Verify image architecture
local image_arch=$(docker inspect --format='{{.Architecture}}' "$full_image" 2>/dev/null || echo "unknown")
local system_arch=$(uname -m)
log_info "Image architecture: $image_arch"
log_info "System architecture: $system_arch"
# Convert system arch format to Docker format for comparison
case "$system_arch" in
x86_64) system_arch="amd64" ;;
aarch64) system_arch="arm64" ;;
esac
if [ "$image_arch" = "$system_arch" ] || [ "$image_arch" = "unknown" ]; then
log_success "Image architecture is compatible"
else
log_warning "Image architecture ($image_arch) may not match system ($system_arch), but multi-arch support should handle this"
fi
}
# Deploy new version
deploy_new_version() {
local image_tag="$1"
log_info "Deploying new version with tag: $image_tag"
# Copy docker-compose file to deployment directory
cp "$PROJECT_ROOT/$DOCKER_COMPOSE_FILE" "$DEPLOY_DIR/"
# Create/update environment file
cat > "$DEPLOY_DIR/.env" << EOF
IMAGE_TAG=$image_tag
REGISTRY=registry.redecarneir.us
MONGODB_CONNECTION_STRING=mongodb://192.168.0.100:27017/BCardsDB
ASPNETCORE_ENVIRONMENT=Release
CERT_PASSWORD=
EOF
# Stop existing containers
cd "$DEPLOY_DIR"
if docker-compose -f "$DOCKER_COMPOSE_FILE" ps -q | grep -q .; then
log_info "Stopping existing containers..."
docker-compose -f "$DOCKER_COMPOSE_FILE" down --remove-orphans
fi
# Start new containers
log_info "Starting new containers..."
docker-compose -f "$DOCKER_COMPOSE_FILE" up -d
# Wait for containers to start
sleep 15
log_success "New version deployed"
}
# Health check
perform_health_check() {
log_info "Performing health check..."
local attempt=1
while [ $attempt -le $MAX_HEALTH_CHECK_ATTEMPTS ]; do
log_info "Health check attempt $attempt/$MAX_HEALTH_CHECK_ATTEMPTS"
# Check if container is running
if ! docker ps --format "table {{.Names}}" | grep -q "$CONTAINER_NAME"; then
log_warning "Container $CONTAINER_NAME is not running"
else
# Check application health endpoint
if curl -f -s "$HEALTH_CHECK_URL" >/dev/null 2>&1; then
log_success "Health check passed"
return 0
fi
# Check if the application is responding on port 80
if curl -f -s "http://localhost:8090/" >/dev/null 2>&1; then
log_success "Application is responding (health endpoint may not be configured)"
return 0
fi
fi
if [ $attempt -eq $MAX_HEALTH_CHECK_ATTEMPTS ]; then
log_error "Health check failed after $MAX_HEALTH_CHECK_ATTEMPTS attempts"
return 1
fi
log_info "Waiting $HEALTH_CHECK_INTERVAL seconds before next attempt..."
sleep $HEALTH_CHECK_INTERVAL
((attempt++))
done
}
# Rollback deployment
rollback_deployment() {
log_warning "Initiating rollback..."
cd "$DEPLOY_DIR"
# Stop current containers
if [ -f "$DOCKER_COMPOSE_FILE" ]; then
docker-compose -f "$DOCKER_COMPOSE_FILE" down --remove-orphans
fi
# Restore previous environment if backup exists
if [ -f ".env.backup" ]; then
mv ".env.backup" ".env"
log_info "Previous environment restored"
fi
# Try to start previous version if image is available
if [ -f ".previous_image" ]; then
local previous_image=$(cat ".previous_image")
log_info "Attempting to restore previous image: $previous_image"
# Update .env with previous image tag
local previous_tag=$(echo "$previous_image" | cut -d':' -f2)
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=$previous_tag/" .env 2>/dev/null || true
# Try to start previous version
if docker-compose -f "$DOCKER_COMPOSE_FILE" up -d; then
log_success "Rollback completed successfully"
else
log_error "Rollback failed - manual intervention required"
fi
else
log_warning "No previous version found for rollback"
fi
}
# Cleanup old images and containers
cleanup_old_resources() {
log_info "Cleaning up old Docker resources..."
# Remove dangling images
if docker images -f "dangling=true" -q | head -1 | grep -q .; then
docker rmi $(docker images -f "dangling=true" -q) || true
log_info "Dangling images removed"
fi
# Remove old backups (keep last 5)
if [ -d "$DEPLOY_DIR/backups" ]; then
find "$DEPLOY_DIR/backups" -maxdepth 1 -type d -name "20*" | sort -r | tail -n +6 | xargs rm -rf || true
log_info "Old backups cleaned up"
fi
log_success "Cleanup completed"
}
# Display deployment summary
display_summary() {
local image_tag="$1"
log_success "Deployment Summary:"
echo "=================================="
echo "🚀 Image Tag: $image_tag"
echo "🌐 Environment: Release (Staging)"
echo "🔗 Application URL: http://localhost:8090"
echo "🔗 Health Check: $HEALTH_CHECK_URL"
echo "🗄️ MongoDB: 192.168.0.100:27017"
echo "📁 Deploy Directory: $DEPLOY_DIR"
echo "🐳 Container: $CONTAINER_NAME"
# Show container status
echo ""
echo "Container Status:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(NAMES|$CONTAINER_NAME)" || true
# Show image info
echo ""
echo "Image Information:"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.CreatedAt}}\t{{.Size}}" | grep -E "(REPOSITORY|bcards)" | head -5 || true
echo "=================================="
}
# Main deployment function
main() {
local image_tag="$1"
log_info "Starting deployment process for BCards Release environment"
log_info "Target image tag: $image_tag"
log_info "Target architecture: $(uname -m)"
log_info "Deploy directory: $DEPLOY_DIR"
# Execute deployment steps
validate_input "$@"
check_prerequisites
test_mongodb_connection
backup_current_deployment
pull_docker_image "$image_tag"
deploy_new_version "$image_tag"
# Perform health check (rollback handled by trap if this fails)
if perform_health_check; then
cleanup_old_resources
display_summary "$image_tag"
log_success "Deployment completed successfully!"
else
log_error "Health check failed - rollback will be triggered"
exit 1
fi
}
# Run main function with all arguments
main "$@"