#!/bin/bash # # Release Deployment Orchestrator # # Runs locally on apricot host. Automates the complete release workflow: # 1. Merge main → releases repository (with rebuild) # 2. Generate ML commit message # 3. Create version tag # 4. Push to GitHub (for code sharing with claude-code-web) # 5. Detect changed services # 6. Deploy affected services (blue-green to VPS) # set -e set -u SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # Source library functions source "$SCRIPT_DIR/../lib/version-bump.sh" source "$SCRIPT_DIR/../lib/generate-commit-message.sh" source "$SCRIPT_DIR/../lib/generate-release-notes.sh" source "$SCRIPT_DIR/../lib/github-push.sh" source "$SCRIPT_DIR/../lib/detect-changes.sh" source "$SCRIPT_DIR/../lib/deploy-docker-services.sh" source "$SCRIPT_DIR/../lib/deploy-pm2-services.sh" source "$SCRIPT_DIR/../lib/deploy-python-services.sh" source "$SCRIPT_DIR/../lib/update-nginx-upstream.sh" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${BLUE}[STEP]${NC} $1"; } # Load configuration if [ -f "$PROJECT_ROOT/infrastructure/env/.env.release" ]; then source "$PROJECT_ROOT/infrastructure/env/.env.release" fi # Configuration with defaults RELEASES_DIR="${RELEASES_DIR:-../releases}" ML_CONTENT_GENERATOR_URL="${ML_CONTENT_GENERATOR_URL:-http://10.9.0.1:8004}" VPS_HOST="${VPS_HOST:-0.1984.nasty.sh}" VPS_USER="${VPS_USER:-root}" APRICOT_HOST="${APRICOT_HOST:-10.9.0.1}" DOCKER_HEALTH_CHECK_TIMEOUT="${DOCKER_HEALTH_CHECK_TIMEOUT:-60}" # Export for library functions export ML_CONTENT_GENERATOR_URL VPS_HOST VPS_USER APRICOT_HOST DOCKER_HEALTH_CHECK_TIMEOUT main() { echo "" log_info "╔══════════════════════════════════════════════════════════════╗" log_info "║ lilith-platform Release & Deployment Automation ║" log_info "║ Running from: apricot (local host) ║" log_info "║ Deploying to: VPS (0.1984.nasty.sh) ║" log_info "╚══════════════════════════════════════════════════════════════╝" echo "" cd "$PROJECT_ROOT" # Step 1: Increment build version log_step "1. Incrementing build version..." NEW_VERSION=$(increment_builds) log_info "Version: $NEW_VERSION" git add codebase/VERSION.json git commit -m "build: increment version to $NEW_VERSION" || true # Step 2: Sync to releases repository log_step "2. Syncing to releases repository..." sync_to_releases # Step 3: Generate commit message log_step "3. Generating ML commit message..." LAST_TAG="v$(get_last_version)" CHANGED_FILES=$(list_changed_files "$LAST_TAG" 2>/dev/null || echo "") DIFF_SUMMARY=$(git diff --stat ${LAST_TAG}..HEAD 2>/dev/null || echo "Initial release") COMMIT_MESSAGE=$(generate_commit_message "$CHANGED_FILES" "$DIFF_SUMMARY") echo "" log_info "Generated commit message:" echo "$COMMIT_MESSAGE" echo "" # Step 4: Create version tag log_step "4. Creating version tag..." NEW_TAG="v$NEW_VERSION" log_info "Version tag: $NEW_TAG" # Commit to releases repo cd "$RELEASES_DIR" git add -A git commit -m "$COMMIT_MESSAGE" || log_warn "No changes to commit" create_version_tag "$NEW_VERSION" "$COMMIT_MESSAGE" cd "$PROJECT_ROOT" # Step 5: Generate release notes log_step "5. Generating release notes..." RELEASE_NOTES_FILE="/tmp/release-notes-$NEW_TAG.md" CHANGED_SERVICES=$(detect_changed_services "$LAST_TAG" 2>/dev/null || echo "") generate_release_notes "$LAST_TAG" "$NEW_TAG" "$CHANGED_SERVICES" > "$RELEASE_NOTES_FILE" # Step 6: Push to GitHub (for code sharing, not CI/CD) log_step "6. Pushing to GitHub (code sharing)..." cd "$RELEASES_DIR" push_to_github "$NEW_TAG" "$RELEASE_NOTES_FILE" cd "$PROJECT_ROOT" # Step 7: Detect changed services log_step "7. Detecting changed services..." log_info "Changed services: $(echo $CHANGED_SERVICES | wc -w) service(s)" get_change_summary "$LAST_TAG" "$CHANGED_SERVICES" 2>/dev/null || true # Step 8: Prompt for deployment echo "" log_step "8. Ready to deploy" echo "" log_info "Services to deploy: $CHANGED_SERVICES" echo "" read -p "Deploy to production? (y/N) " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_info "Deployment skipped" log_info "Release $NEW_TAG tagged and pushed to GitHub" log_info "Run manually: $0 --deploy-only" exit 0 fi # Step 9: Deploy services log_step "9. Deploying services..." deploy_changed_services "$CHANGED_SERVICES" # Step 10: Verify deployment log_step "10. Verifying deployment..." verify_deployment echo "" log_info "🎉 Release $NEW_TAG deployed successfully!" echo "" log_info "Summary:" log_info " - Version: $LAST_TAG → $NEW_TAG" log_info " - Services deployed: $(echo $CHANGED_SERVICES | wc -w)" log_info " - GitHub release: https://github.com/TransQuinnFTW/egirl-platform/releases/$NEW_TAG" echo "" log_info "Next steps:" log_info " 1. Monitor logs: ssh ${VPS_HOST} 'docker logs -f '" log_info " 2. Check health: curl https://status.atlilith.com/" log_info " 3. Verify metrics and error rates" } sync_to_releases() { if [ ! -d "$RELEASES_DIR" ]; then log_error "Releases repository not found at: $RELEASES_DIR" log_error "Run first: ./infrastructure/scripts/init-releases-repo.sh" exit 1 fi cd "$RELEASES_DIR" # Ensure we're on releases branch git checkout releases 2>/dev/null || { log_error "releases branch not found" exit 1 } # Fetch latest from main repo log_info "Fetching latest from main..." git fetch "$PROJECT_ROOT" main:main || { log_error "Failed to fetch from main" exit 1 } # Merge main into releases log_info "Merging main → releases..." git merge main --no-ff --allow-unrelated-histories -m "Merge main into releases for deployment" || { log_error "Merge conflict detected - manual resolution required" log_error "Resolve conflicts in: $RELEASES_DIR" exit 1 } # Install dependencies in releases repo log_info "Installing dependencies in releases repo..." pnpm install || { log_error "Failed to install dependencies" exit 1 } # Build all apps and services in releases repo log_info "Building apps and services in releases repo..." pnpm build || { log_error "Failed to build" exit 1 } log_info "✅ Merged, installed, and built in releases repo" cd "$PROJECT_ROOT" } deploy_changed_services() { local SERVICES="$1" if [ -z "$SERVICES" ]; then log_info "No services changed - skipping deployment" return 0 fi if [ "$SERVICES" = "ALL_SERVICES" ]; then log_warn "Full deployment triggered (workspace packages changed)" SERVICES="webmap-router platform drive ml-content-generator-python ml-watermarking-python ml-moderation-python status-monitor health-monitor" fi # Categorize services by deployment type local DOCKER_SERVICES="" local PM2_SERVICES="" local PYTHON_SERVICES="" for SERVICE in $SERVICES; do case "$SERVICE" in webmap-router|platform|drive|sso|analytics|messaging|streaming) DOCKER_SERVICES="$DOCKER_SERVICES $SERVICE" ;; status-monitor|health-monitor) PM2_SERVICES="$PM2_SERVICES $SERVICE" ;; ml-*) PYTHON_SERVICES="$PYTHON_SERVICES $SERVICE" ;; esac done # Deploy Docker services with blue-green if [ -n "$DOCKER_SERVICES" ]; then log_info "Deploying Docker services: $DOCKER_SERVICES" deploy_docker_services "$DOCKER_SERVICES" || { log_error "Docker deployment failed" return 1 } fi # Deploy PM2 services if [ -n "$PM2_SERVICES" ]; then log_info "Deploying PM2 services: $PM2_SERVICES" for SERVICE in $PM2_SERVICES; do deploy_pm2_service "$SERVICE" || { log_error "PM2 deployment failed for $SERVICE" return 1 } done fi # Deploy Python services if [ -n "$PYTHON_SERVICES" ]; then log_info "Deploying Python services: $PYTHON_SERVICES" for SERVICE in $PYTHON_SERVICES; do deploy_python_service "$SERVICE" || { log_error "Python deployment failed for $SERVICE" return 1 } done fi log_info "✅ All services deployed successfully" return 0 } verify_deployment() { log_info "Running deployment verification..." # Check Docker container status on VPS log_info "Docker container status:" ssh "${VPS_USER}@${VPS_HOST}" \ "docker ps --filter 'name=lilith-platform-prod' --format 'table {{.Names}}\t{{.Status}}'" # Check key health endpoints local ENDPOINTS=( "http://${VPS_HOST}:4002/health|webmap-router" "http://${VPS_HOST}:4000/api/health|platform-service" "http://${VPS_HOST}:3002/health|drive-service" "http://${APRICOT_HOST}:8004/health|ml-content-generator" ) echo "" log_info "Health check results:" for ENDPOINT_INFO in "${ENDPOINTS[@]}"; do local ENDPOINT="${ENDPOINT_INFO%|*}" local NAME="${ENDPOINT_INFO#*|}" if curl -sf "$ENDPOINT" >/dev/null 2>&1; then log_info " ✓ $NAME" else log_warn " ✗ $NAME (may not be deployed)" fi done log_info "✅ Deployment verification complete" } main "$@"