Files
whale-town/deploy.sh
2025-12-05 19:00:14 +08:00

330 lines
8.2 KiB
Bash

#!/bin/bash
# AI Town Game Production Deployment Script
# Usage: ./deploy.sh [environment]
set -e
# Configuration
ENVIRONMENT=${1:-production}
PROJECT_NAME="ai-town-game"
BACKUP_DIR="/opt/backups/$PROJECT_NAME"
DEPLOY_DIR="/opt/$PROJECT_NAME"
WEB_DIR="$DEPLOY_DIR/web"
SERVER_DIR="$DEPLOY_DIR/server"
# 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
check_root() {
if [[ $EUID -eq 0 ]]; then
error "This script should not be run as root"
exit 1
fi
}
# Check system requirements
check_requirements() {
log "Checking system requirements..."
# Check Docker
if ! command -v docker &> /dev/null; then
error "Docker is not installed"
exit 1
fi
# Check Docker Compose
if ! command -v docker-compose &> /dev/null; then
error "Docker Compose is not installed"
exit 1
fi
# Check Node.js (for local builds)
if ! command -v node &> /dev/null; then
warning "Node.js is not installed (required for local builds)"
fi
# Check available disk space (at least 2GB)
AVAILABLE_SPACE=$(df / | tail -1 | awk '{print $4}')
if [[ $AVAILABLE_SPACE -lt 2097152 ]]; then
error "Insufficient disk space (need at least 2GB)"
exit 1
fi
success "System requirements check passed"
}
# Create backup of current deployment
create_backup() {
log "Creating backup of current deployment..."
if [[ -d "$DEPLOY_DIR" ]]; then
BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup data directory
if [[ -d "$SERVER_DIR/data" ]]; then
tar -czf "$BACKUP_DIR/${BACKUP_NAME}_data.tar.gz" -C "$SERVER_DIR" data/
success "Data backup created: ${BACKUP_NAME}_data.tar.gz"
fi
# Backup configuration
if [[ -f "$DEPLOY_DIR/.env" ]]; then
cp "$DEPLOY_DIR/.env" "$BACKUP_DIR/${BACKUP_NAME}_env"
success "Configuration backup created: ${BACKUP_NAME}_env"
fi
# Clean old backups (keep last 5)
cd "$BACKUP_DIR"
ls -t backup_*_data.tar.gz 2>/dev/null | tail -n +6 | xargs rm -f
ls -t backup_*_env 2>/dev/null | tail -n +6 | xargs rm -f
else
log "No existing deployment found, skipping backup"
fi
}
# Setup deployment directory
setup_directories() {
log "Setting up deployment directories..."
sudo mkdir -p "$DEPLOY_DIR"
sudo mkdir -p "$WEB_DIR"
sudo mkdir -p "$SERVER_DIR"
sudo mkdir -p "$BACKUP_DIR"
sudo mkdir -p "/var/log/$PROJECT_NAME"
# Set permissions
sudo chown -R $USER:$USER "$DEPLOY_DIR"
sudo chown -R $USER:$USER "$BACKUP_DIR"
success "Directories created and configured"
}
# Deploy server application
deploy_server() {
log "Deploying server application..."
# Copy server files
cp -r server/* "$SERVER_DIR/"
# Copy Docker configuration
cp docker-compose.prod.yml "$DEPLOY_DIR/"
cp nginx/nginx.conf "$DEPLOY_DIR/nginx/"
# Setup environment configuration
if [[ ! -f "$DEPLOY_DIR/.env" ]]; then
cp .env.production "$DEPLOY_DIR/.env"
warning "Please edit $DEPLOY_DIR/.env with your production settings"
fi
# Build and start services
cd "$DEPLOY_DIR"
docker-compose -f docker-compose.prod.yml build
docker-compose -f docker-compose.prod.yml up -d
success "Server application deployed"
}
# Deploy web client
deploy_web() {
log "Deploying web client..."
# Check if web build exists
if [[ ! -d "web" ]]; then
error "Web build not found. Please export from Godot first."
exit 1
fi
# Copy web files
cp -r web/* "$WEB_DIR/"
# Set proper permissions
sudo chown -R www-data:www-data "$WEB_DIR"
sudo chmod -R 755 "$WEB_DIR"
success "Web client deployed"
}
# Configure system services
configure_services() {
log "Configuring system services..."
# Create systemd service for monitoring
sudo tee /etc/systemd/system/ai-town-monitor.service > /dev/null <<EOF
[Unit]
Description=AI Town Game Monitor
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/opt/ai-town-game/monitor.sh
User=$USER
[Install]
WantedBy=multi-user.target
EOF
# Create monitoring script
tee "$DEPLOY_DIR/monitor.sh" > /dev/null <<'EOF'
#!/bin/bash
cd /opt/ai-town-game
if ! docker-compose -f docker-compose.prod.yml ps | grep -q "Up"; then
echo "AI Town services are down, restarting..."
docker-compose -f docker-compose.prod.yml up -d
fi
EOF
chmod +x "$DEPLOY_DIR/monitor.sh"
# Setup cron job for monitoring
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/ai-town-game/monitor.sh") | crontab -
success "System services configured"
}
# Setup SSL certificates (Let's Encrypt)
setup_ssl() {
if [[ -z "$DOMAIN" ]]; then
warning "DOMAIN not set, skipping SSL setup"
return
fi
log "Setting up SSL certificates for $DOMAIN..."
# Install certbot
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
# Get certificate
sudo certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "$EMAIL"
# Setup auto-renewal
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
success "SSL certificates configured"
}
# Health check
health_check() {
log "Performing health check..."
# Wait for services to start
sleep 30
# Check server health
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
success "Server health check passed"
else
error "Server health check failed"
return 1
fi
# Check web client
if curl -f http://localhost/ > /dev/null 2>&1; then
success "Web client health check passed"
else
error "Web client health check failed"
return 1
fi
# Check admin API
if curl -f http://localhost:8081/api/status > /dev/null 2>&1; then
success "Admin API health check passed"
else
warning "Admin API health check failed (may require authentication)"
fi
success "Health check completed"
}
# Rollback function
rollback() {
error "Deployment failed, initiating rollback..."
cd "$DEPLOY_DIR"
docker-compose -f docker-compose.prod.yml down
# Restore from latest backup
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/backup_*_data.tar.gz 2>/dev/null | head -1)
if [[ -n "$LATEST_BACKUP" ]]; then
log "Restoring from backup: $LATEST_BACKUP"
tar -xzf "$LATEST_BACKUP" -C "$SERVER_DIR"
fi
# Restart services
docker-compose -f docker-compose.prod.yml up -d
error "Rollback completed"
exit 1
}
# Main deployment function
main() {
log "Starting AI Town Game deployment (Environment: $ENVIRONMENT)"
# Trap errors for rollback
trap rollback ERR
check_root
check_requirements
create_backup
setup_directories
deploy_server
deploy_web
configure_services
# Setup SSL if domain is provided
if [[ -n "$DOMAIN" ]]; then
setup_ssl
fi
# Health check
if ! health_check; then
rollback
fi
success "Deployment completed successfully!"
log "Access your application at:"
log " Web Client: http://localhost/ (or https://$DOMAIN)"
log " Admin Panel: http://localhost:8081/admin/"
log " Server API: http://localhost:8080/"
log "Important files:"
log " Configuration: $DEPLOY_DIR/.env"
log " Logs: /var/log/$PROJECT_NAME/"
log " Backups: $BACKUP_DIR/"
log "Next steps:"
log " 1. Edit $DEPLOY_DIR/.env with your production settings"
log " 2. Configure your domain DNS to point to this server"
log " 3. Set up monitoring and alerting"
log " 4. Test all functionality"
}
# Script entry point
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi