#!/bin/bash # Quick deployment script for fetch_ml set -e SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) export FETCHML_REPO_ROOT="${FETCHML_REPO_ROOT:-${REPO_ROOT}}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored output print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Function to show usage show_usage() { echo "Usage: $0 [ENVIRONMENT] [ACTION]" echo "" echo "Environments:" echo " dev Development environment" echo " staging Staging environment (pre-production)" echo " secure Secure homelab environment" echo " prod Production environment" echo "" echo "Actions:" echo " up Start services" echo " down Stop services" echo " restart Restart services" echo " logs Show logs" echo " status Show status" echo " rollback Rollback to previous deployment (image only)" echo " health-check Check service health and compliance mode" echo " check-audit-sink Verify audit sink reachability" echo "" echo "Examples:" echo " $0 dev up # Start development environment" echo " $0 staging up # Start staging environment" echo " $0 prod down # Stop production environment" echo " $0 staging rollback # Rollback staging deployment" echo " $0 prod health-check # Check production health" echo " $0 prod check-audit-sink # Verify audit sink before deploy" } # Function to check if docker-compose file exists check_compose_file() { local env=$1 local compose_file="" case $env in "dev") compose_file="${FETCHML_REPO_ROOT}/deployments/docker-compose.dev.yml" ;; "staging") compose_file="${FETCHML_REPO_ROOT}/deployments/docker-compose.staging.yml" ;; "secure") compose_file="${FETCHML_REPO_ROOT}/deployments/docker-compose.homelab-secure.yml" ;; "prod") compose_file="${FETCHML_REPO_ROOT}/deployments/docker-compose.prod.yml" ;; *) print_error "Unknown environment: $env" show_usage exit 1 ;; esac if [ ! -f "$compose_file" ]; then print_error "Docker Compose file not found: $compose_file" exit 1 fi echo "$compose_file" } # Function to check if .env file exists check_env_file() { local env=$1 if [ ! -f "${FETCHML_REPO_ROOT}/.env" ]; then print_warning ".env file not found. Creating from example..." if [ "$env" = "dev" ]; then cp "${FETCHML_REPO_ROOT}/deployments/env.dev.example" "${FETCHML_REPO_ROOT}/.env" elif [ "$env" = "prod" ]; then cp "${FETCHML_REPO_ROOT}/deployments/env.prod.example" "${FETCHML_REPO_ROOT}/.env" else cp "${FETCHML_REPO_ROOT}/deployments/env.dev.example" "${FETCHML_REPO_ROOT}/.env" fi print_warning "Please edit .env file with your configuration" fi } # Main script main() { if [ $# -ne 2 ]; then show_usage exit 1 fi local environment=$1 local action=$2 print_status "Environment: $environment" print_status "Action: $action" # Check compose file compose_file=$(check_compose_file "$environment") print_status "Using: $compose_file" # Check .env file check_env_file "$environment" # Execute action case $action in "up") print_status "Starting $environment environment..." docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" up -d print_success "$environment environment started successfully!" # Show service URLs echo "" print_status "Service URLs:" echo " API Server: http://localhost:9101" if [ "$environment" = "dev" ]; then echo " Grafana: http://localhost:3000 (admin/admin123)" echo " Prometheus: http://localhost:9090" fi ;; "down") print_status "Stopping $environment environment..." docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" down print_success "$environment environment stopped successfully!" ;; "restart") print_status "Restarting $environment environment..." docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" restart print_success "$environment environment restarted successfully!" ;; "logs") print_status "Showing logs for $environment environment..." docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" logs -f ;; "status") print_status "Status of $environment environment:" docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" ps ;; "rollback") print_warning "Rolling back $environment environment..." print_warning "⚠ This rolls back the image only - queue state and audit log are NOT rolled back" if [ "$environment" = "prod" ]; then print_warning "⚠ CRITICAL: Production rollback" print_warning "⚠ Queue state is NOT rolled back" print_warning "⚠ Audit log chain is NOT rolled back (must never break chain)" read -p "CONFIRM PRODUCTION ROLLBACK? [yes/N] " confirm if [ "$confirm" != "yes" ]; then print_error "Rollback cancelled" exit 1 fi fi # Get previous deployment info LOG_FILE="${FETCHML_REPO_ROOT}/deployments/.${environment}-audit.log" if [ -f "$LOG_FILE" ]; then PREVIOUS_SHA=$(tail -2 "$LOG_FILE" | head -1 | grep -o 'sha-[a-f0-9]*' || echo "") if [ -n "$PREVIOUS_SHA" ]; then print_status "Rolling back to: $PREVIOUS_SHA" fi fi docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" down docker-compose --project-directory "${FETCHML_REPO_ROOT}" -f "$compose_file" up -d # Write rollback entry to audit log echo "$(date -Iseconds) | rollback | $environment | actor=$(whoami)" >> "$LOG_FILE" 2>/dev/null || true print_success "$environment rollback complete!" print_status "Verify health with: $0 $environment health-check" ;; "health-check"|"health") print_status "Health check for $environment environment..." # Determine port based on environment case $environment in dev) PORT=9101 ;; staging) PORT=9102 ;; prod) PORT=9101 ;; *) PORT=9101 ;; esac # Check API health if curl -fsS "http://localhost:${PORT}/health" > /dev/null 2>&1; then print_success "API is healthy (port $PORT)" # Check compliance_mode COMPLIANCE_MODE=$(curl -fsS "http://localhost:${PORT}/health" 2>/dev/null | grep -o '"compliance_mode":"[^"]*"' | cut -d'"' -f4 || echo "unknown") print_status "Compliance mode: $COMPLIANCE_MODE" else print_error "API health check failed (port $PORT)" exit 1 fi ;; "check-audit-sink") print_status "Checking audit sink for $environment..." if [ -f "${FETCHML_REPO_ROOT}/scripts/check-audit-sink.sh" ]; then "${FETCHML_REPO_ROOT}/scripts/check-audit-sink.sh" --env "$environment" else print_warning "Audit sink check script not found" fi ;; *) print_error "Unknown action: $action" show_usage exit 1 ;; esac } # Run main function main "$@"