330 lines
8.2 KiB
Bash
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 |