|
|
#!/bin/bash |
|
|
|
|
|
|
|
|
|
|
|
set -e |
|
|
|
|
|
|
|
|
SCRIPT_DIR=$(dirname $(realpath $0)) |
|
|
PROJECT_ROOT=$(dirname $SCRIPT_DIR) |
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S) |
|
|
|
|
|
|
|
|
RED='\033[0;31m' |
|
|
GREEN='\033[0;32m' |
|
|
YELLOW='\033[1;33m' |
|
|
BLUE='\033[0;34m' |
|
|
NC='\033[0m' |
|
|
|
|
|
|
|
|
ENVIRONMENT="production" |
|
|
DEPLOY_METHOD="docker" |
|
|
REGISTRY="" |
|
|
VERSION="latest" |
|
|
BACKUP=true |
|
|
|
|
|
|
|
|
while [[ $# -gt 0 ]]; do |
|
|
case $1 in |
|
|
--env) |
|
|
ENVIRONMENT="$2" |
|
|
shift 2 |
|
|
;; |
|
|
--method) |
|
|
DEPLOY_METHOD="$2" |
|
|
shift 2 |
|
|
;; |
|
|
--registry) |
|
|
REGISTRY="$2" |
|
|
shift 2 |
|
|
;; |
|
|
--version) |
|
|
VERSION="$2" |
|
|
shift 2 |
|
|
;; |
|
|
--no-backup) |
|
|
BACKUP=false |
|
|
shift |
|
|
;; |
|
|
--help) |
|
|
show_help |
|
|
exit 0 |
|
|
;; |
|
|
*) |
|
|
echo -e "${RED}Unknown option: $1${NC}" |
|
|
show_help |
|
|
exit 1 |
|
|
;; |
|
|
esac |
|
|
done |
|
|
|
|
|
show_help() { |
|
|
cat << EOF |
|
|
Usage: $0 [OPTIONS] |
|
|
|
|
|
Deploy BackgroundFX Pro to various environments |
|
|
|
|
|
Options: |
|
|
--env ENV Environment (development, staging, production) [default: production] |
|
|
--method METHOD Deployment method (docker, kubernetes, server) [default: docker] |
|
|
--registry REGISTRY Container registry URL |
|
|
--version VERSION Version to deploy [default: latest] |
|
|
--no-backup Skip backup before deployment |
|
|
--help Show this help message |
|
|
|
|
|
Examples: |
|
|
$0 --env production --method docker |
|
|
$0 --env staging --registry myregistry.com --version 1.0.0 |
|
|
$0 --env development --no-backup |
|
|
EOF |
|
|
} |
|
|
|
|
|
|
|
|
deploy_docker() { |
|
|
echo -e "${BLUE}Deploying with Docker to ${ENVIRONMENT}...${NC}" |
|
|
|
|
|
cd "$PROJECT_ROOT" |
|
|
|
|
|
|
|
|
echo "Building Docker images..." |
|
|
if [ "$ENVIRONMENT" = "production" ]; then |
|
|
docker build -f docker/Dockerfile.prod -t backgroundfx-pro:$VERSION . |
|
|
else |
|
|
docker build -f docker/Dockerfile -t backgroundfx-pro:$VERSION . |
|
|
fi |
|
|
|
|
|
|
|
|
if [ -n "$REGISTRY" ]; then |
|
|
docker tag backgroundfx-pro:$VERSION $REGISTRY/backgroundfx-pro:$VERSION |
|
|
docker push $REGISTRY/backgroundfx-pro:$VERSION |
|
|
echo -e "${GREEN}β Pushed to registry: $REGISTRY${NC}" |
|
|
fi |
|
|
|
|
|
|
|
|
if [ "$ENVIRONMENT" = "production" ]; then |
|
|
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d |
|
|
else |
|
|
docker-compose up -d |
|
|
fi |
|
|
|
|
|
echo -e "${GREEN}β Docker deployment complete${NC}" |
|
|
} |
|
|
|
|
|
deploy_kubernetes() { |
|
|
echo -e "${BLUE}Deploying to Kubernetes cluster...${NC}" |
|
|
|
|
|
cd "$PROJECT_ROOT/kubernetes" |
|
|
|
|
|
|
|
|
if ! command -v kubectl &> /dev/null; then |
|
|
echo -e "${RED}kubectl not found${NC}" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
|
|
|
echo "Applying Kubernetes configurations..." |
|
|
|
|
|
|
|
|
kubectl create namespace backgroundfx --dry-run=client -o yaml | kubectl apply -f - |
|
|
|
|
|
|
|
|
if [ "$ENVIRONMENT" = "production" ]; then |
|
|
kubectl apply -f configmap-prod.yaml |
|
|
kubectl apply -f secret-prod.yaml |
|
|
kubectl apply -f deployment-prod.yaml |
|
|
kubectl apply -f service-prod.yaml |
|
|
kubectl apply -f ingress-prod.yaml |
|
|
else |
|
|
kubectl apply -f configmap.yaml |
|
|
kubectl apply -f deployment.yaml |
|
|
kubectl apply -f service.yaml |
|
|
fi |
|
|
|
|
|
|
|
|
echo "Waiting for deployment to be ready..." |
|
|
kubectl rollout status deployment/backgroundfx-pro -n backgroundfx |
|
|
|
|
|
|
|
|
echo -e "\n${GREEN}Deployment complete!${NC}" |
|
|
kubectl get services -n backgroundfx |
|
|
kubectl get pods -n backgroundfx |
|
|
} |
|
|
|
|
|
deploy_server() { |
|
|
echo -e "${BLUE}Deploying to server...${NC}" |
|
|
|
|
|
|
|
|
if [ -z "$DEPLOY_HOST" ] || [ -z "$DEPLOY_USER" ]; then |
|
|
echo -e "${RED}Error: DEPLOY_HOST and DEPLOY_USER must be set${NC}" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
cd "$PROJECT_ROOT" |
|
|
|
|
|
|
|
|
echo "Creating deployment package..." |
|
|
PACKAGE_NAME="backgroundfx_${VERSION}_${TIMESTAMP}.tar.gz" |
|
|
|
|
|
tar -czf "/tmp/$PACKAGE_NAME" \ |
|
|
--exclude="*.pyc" \ |
|
|
--exclude="__pycache__" \ |
|
|
--exclude=".git" \ |
|
|
--exclude="venv" \ |
|
|
--exclude="*.log" \ |
|
|
--exclude="outputs/*" \ |
|
|
--exclude="uploads/*" \ |
|
|
. |
|
|
|
|
|
|
|
|
echo "Uploading to $DEPLOY_HOST..." |
|
|
scp "/tmp/$PACKAGE_NAME" "$DEPLOY_USER@$DEPLOY_HOST:/tmp/" |
|
|
|
|
|
|
|
|
echo "Deploying on server..." |
|
|
ssh "$DEPLOY_USER@$DEPLOY_HOST" << EOF |
|
|
# Create deployment directory |
|
|
mkdir -p /opt/backgroundfx/releases/$VERSION |
|
|
|
|
|
# Extract package |
|
|
tar -xzf /tmp/$PACKAGE_NAME -C /opt/backgroundfx/releases/$VERSION |
|
|
|
|
|
# Stop current service |
|
|
sudo systemctl stop backgroundfx || true |
|
|
|
|
|
# Update symlink |
|
|
rm -f /opt/backgroundfx/current |
|
|
ln -s /opt/backgroundfx/releases/$VERSION /opt/backgroundfx/current |
|
|
|
|
|
# Install dependencies |
|
|
cd /opt/backgroundfx/current |
|
|
python3 -m venv venv |
|
|
source venv/bin/activate |
|
|
pip install -r requirements.txt |
|
|
|
|
|
# Start service |
|
|
sudo systemctl start backgroundfx |
|
|
sudo systemctl status backgroundfx |
|
|
EOF |
|
|
|
|
|
|
|
|
rm "/tmp/$PACKAGE_NAME" |
|
|
|
|
|
echo -e "${GREEN}β Server deployment complete${NC}" |
|
|
} |
|
|
|
|
|
|
|
|
health_check() { |
|
|
echo -e "\n${BLUE}Running health check...${NC}" |
|
|
|
|
|
|
|
|
if [ "$ENVIRONMENT" = "production" ]; then |
|
|
URL="https://app.backgroundfx.com/health" |
|
|
elif [ "$ENVIRONMENT" = "staging" ]; then |
|
|
URL="https://staging.backgroundfx.com/health" |
|
|
else |
|
|
URL="http://localhost:7860/health" |
|
|
fi |
|
|
|
|
|
|
|
|
if curl -f -s "$URL" > /dev/null; then |
|
|
echo -e "${GREEN}β Application is healthy${NC}" |
|
|
curl -s "$URL" | python -m json.tool |
|
|
else |
|
|
echo -e "${RED}β Health check failed${NC}" |
|
|
return 1 |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
create_backup() { |
|
|
if [ "$BACKUP" = false ]; then |
|
|
return |
|
|
fi |
|
|
|
|
|
echo -e "${BLUE}Creating backup...${NC}" |
|
|
|
|
|
BACKUP_DIR="$PROJECT_ROOT/backups/$TIMESTAMP" |
|
|
mkdir -p "$BACKUP_DIR" |
|
|
|
|
|
|
|
|
if [ -f "$PROJECT_ROOT/database.db" ]; then |
|
|
cp "$PROJECT_ROOT/database.db" "$BACKUP_DIR/" |
|
|
fi |
|
|
|
|
|
|
|
|
cp -r "$PROJECT_ROOT/config" "$BACKUP_DIR/" 2>/dev/null || true |
|
|
|
|
|
|
|
|
if [ "$DEPLOY_METHOD" = "docker" ]; then |
|
|
docker run --rm \ |
|
|
-v backgroundfx-pro_model-cache:/data \ |
|
|
-v "$BACKUP_DIR":/backup \ |
|
|
alpine tar czf /backup/models.tar.gz -C /data . 2>/dev/null || true |
|
|
fi |
|
|
|
|
|
echo -e "${GREEN}β Backup created: $BACKUP_DIR${NC}" |
|
|
} |
|
|
|
|
|
|
|
|
rollback() { |
|
|
echo -e "${YELLOW}Rolling back deployment...${NC}" |
|
|
|
|
|
if [ "$DEPLOY_METHOD" = "docker" ]; then |
|
|
|
|
|
docker-compose down |
|
|
docker tag backgroundfx-pro:previous backgroundfx-pro:$VERSION |
|
|
docker-compose up -d |
|
|
elif [ "$DEPLOY_METHOD" = "kubernetes" ]; then |
|
|
|
|
|
kubectl rollout undo deployment/backgroundfx-pro -n backgroundfx |
|
|
elif [ "$DEPLOY_METHOD" = "server" ]; then |
|
|
|
|
|
ssh "$DEPLOY_USER@$DEPLOY_HOST" << EOF |
|
|
rm /opt/backgroundfx/current |
|
|
ln -s /opt/backgroundfx/releases/previous /opt/backgroundfx/current |
|
|
sudo systemctl restart backgroundfx |
|
|
EOF |
|
|
fi |
|
|
|
|
|
echo -e "${GREEN}β Rollback complete${NC}" |
|
|
} |
|
|
|
|
|
|
|
|
pre_deploy_checks() { |
|
|
echo -e "${BLUE}Running pre-deployment checks...${NC}" |
|
|
|
|
|
|
|
|
if [ -d .git ]; then |
|
|
if [ -n "$(git status --porcelain)" ]; then |
|
|
echo -e "${YELLOW}Warning: Uncommitted changes detected${NC}" |
|
|
read -p "Continue anyway? (y/n): " -n 1 -r |
|
|
echo |
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|
|
exit 1 |
|
|
fi |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
echo "Running tests..." |
|
|
if command -v pytest &> /dev/null; then |
|
|
pytest tests/ -m "not slow" --quiet || { |
|
|
echo -e "${RED}Tests failed${NC}" |
|
|
read -p "Deploy anyway? (y/n): " -n 1 -r |
|
|
echo |
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|
|
exit 1 |
|
|
fi |
|
|
} |
|
|
else |
|
|
echo -e "${YELLOW}pytest not found, skipping tests${NC}" |
|
|
fi |
|
|
|
|
|
echo -e "${GREEN}β Pre-deployment checks passed${NC}" |
|
|
} |
|
|
|
|
|
|
|
|
main() { |
|
|
echo -e "${BLUE}========================================${NC}" |
|
|
echo -e "${BLUE}BackgroundFX Pro Deployment${NC}" |
|
|
echo -e "${BLUE}========================================${NC}" |
|
|
echo "Environment: $ENVIRONMENT" |
|
|
echo "Method: $DEPLOY_METHOD" |
|
|
echo "Version: $VERSION" |
|
|
echo |
|
|
|
|
|
|
|
|
pre_deploy_checks |
|
|
|
|
|
|
|
|
create_backup |
|
|
|
|
|
|
|
|
case $DEPLOY_METHOD in |
|
|
docker) |
|
|
deploy_docker |
|
|
;; |
|
|
kubernetes) |
|
|
deploy_kubernetes |
|
|
;; |
|
|
server) |
|
|
deploy_server |
|
|
;; |
|
|
*) |
|
|
echo -e "${RED}Invalid deployment method: $DEPLOY_METHOD${NC}" |
|
|
exit 1 |
|
|
;; |
|
|
esac |
|
|
|
|
|
|
|
|
sleep 5 |
|
|
if health_check; then |
|
|
echo -e "\n${GREEN}========================================${NC}" |
|
|
echo -e "${GREEN}Deployment Successful!${NC}" |
|
|
echo -e "${GREEN}========================================${NC}" |
|
|
else |
|
|
echo -e "\n${RED}Deployment may have issues${NC}" |
|
|
read -p "Rollback? (y/n): " -n 1 -r |
|
|
echo |
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then |
|
|
rollback |
|
|
fi |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
trap 'echo -e "${RED}Deployment failed!${NC}"; exit 1' ERR |
|
|
|
|
|
|
|
|
main |