2025-12-25 23:03:57 -08:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
|
# Rectifier Deployment Script
|
|
|
|
|
|
#
|
|
|
|
|
|
# Unified auto-deploy that detects changed components and deploys them.
|
|
|
|
|
|
# Called by pre-push hook after pushing to main.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Components handled:
|
|
|
|
|
|
# - status-dashboard → deploy-status-dashboard.sh
|
|
|
|
|
|
# - service-registry → deploy-service-registry.sh
|
|
|
|
|
|
# - Platform services → release-deploy.sh (full pipeline)
|
|
|
|
|
|
#
|
|
|
|
|
|
# Usage: ./rectify-deploy.sh [--dry-run]
|
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
# Source dependency graph library (dynamic detection from package.json files)
|
|
|
|
|
|
source "$SCRIPT_DIR/lib/dependency-graph.sh" 2>/dev/null || {
|
|
|
|
|
|
echo "Warning: dependency-graph.sh not found, using fallback detection"
|
|
|
|
|
|
FALLBACK_DETECTION=true
|
2025-12-25 23:03:57 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Inline logging
|
|
|
|
|
|
log_header() { echo -e "\n\033[1;35m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m"; echo -e "\033[1;35m $1\033[0m"; echo -e "\033[1;35m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m"; }
|
|
|
|
|
|
log_step() { echo -e "\n\033[1;34m▶\033[0m $1"; }
|
|
|
|
|
|
log_info() { echo -e "\033[0;36m ℹ\033[0m $1"; }
|
|
|
|
|
|
log_success() { echo -e "\033[0;32m ✓\033[0m $1"; }
|
|
|
|
|
|
log_error() { echo -e "\033[0;31m ✗\033[0m $1"; }
|
|
|
|
|
|
log_warn() { echo -e "\033[0;33m ⚠\033[0m $1"; }
|
|
|
|
|
|
|
|
|
|
|
|
DRY_RUN="${1:-}"
|
|
|
|
|
|
|
2025-12-27 23:11:24 -08:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# SSL CERTIFICATE CHECK
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
check_ssl_certificates() {
|
|
|
|
|
|
log_step "Checking SSL certificates..."
|
|
|
|
|
|
|
|
|
|
|
|
local reconcile_dir="${PROJECT_ROOT}/infrastructure/reconciliation"
|
|
|
|
|
|
|
|
|
|
|
|
if [[ ! -x "$reconcile_dir/reconcile" ]]; then
|
|
|
|
|
|
log_warn "Reconciliation system not available, skipping SSL check"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$DRY_RUN" = "--dry-run" ]; then
|
|
|
|
|
|
log_info "[DRY RUN] Would check SSL certificates on VPS"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
"$reconcile_dir/reconcile" --host vps --service ssl-certificate || {
|
|
|
|
|
|
log_error "SSL certificate check/renewal failed"
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log_success "SSL certificates OK"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-25 23:03:57 -08:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# DETECT CHANGES
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
detect_all_changes() {
|
2025-12-25 23:28:34 -08:00
|
|
|
|
# All logging in this function goes to stderr since stdout is captured
|
|
|
|
|
|
log_step "Detecting changed components..." >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
|
|
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
|
|
|
|
|
|
|
|
# Get list of changed files since last push
|
|
|
|
|
|
local CHANGED_FILES
|
|
|
|
|
|
CHANGED_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null || git diff --name-only HEAD)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$CHANGED_FILES" ]; then
|
2025-12-25 23:28:34 -08:00
|
|
|
|
log_info "No changes detected" >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
local file_count
|
|
|
|
|
|
file_count=$(echo "$CHANGED_FILES" | wc -l)
|
|
|
|
|
|
log_info "Changed files: $file_count" >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
# Use dynamic dependency detection from package.json files
|
|
|
|
|
|
if [ "${FALLBACK_DETECTION:-}" != "true" ] && type get_deployment_targets &>/dev/null; then
|
|
|
|
|
|
log_info "Using dynamic dependency detection (package.json)" >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
local COMPONENTS
|
|
|
|
|
|
COMPONENTS=$(get_deployment_targets "$CHANGED_FILES")
|
|
|
|
|
|
|
|
|
|
|
|
# Log what was detected
|
|
|
|
|
|
for comp in $COMPONENTS; do
|
|
|
|
|
|
log_info " Deploy target: $comp" >&2
|
|
|
|
|
|
done
|
2025-12-25 23:03:57 -08:00
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
echo "$COMPONENTS"
|
|
|
|
|
|
else
|
|
|
|
|
|
# Fallback: pattern-based detection
|
|
|
|
|
|
log_warn "Using fallback pattern detection" >&2
|
|
|
|
|
|
|
|
|
|
|
|
local COMPONENTS=""
|
|
|
|
|
|
|
|
|
|
|
|
# Direct target changes
|
|
|
|
|
|
if echo "$CHANGED_FILES" | grep -q "^features/status-dashboard/"; then
|
2025-12-25 23:03:57 -08:00
|
|
|
|
COMPONENTS="$COMPONENTS status-dashboard"
|
2025-12-25 23:28:34 -08:00
|
|
|
|
log_info " Direct: status-dashboard" >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
fi
|
2025-12-25 23:28:34 -08:00
|
|
|
|
|
|
|
|
|
|
if echo "$CHANGED_FILES" | grep -q "^infrastructure/service-registry/"; then
|
2025-12-25 23:03:57 -08:00
|
|
|
|
COMPONENTS="$COMPONENTS service-registry"
|
2025-12-25 23:28:34 -08:00
|
|
|
|
log_info " Direct: service-registry" >&2
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# UI packages affect all UI consumers
|
|
|
|
|
|
if echo "$CHANGED_FILES" | grep -q "^@packages/@ui/"; then
|
|
|
|
|
|
COMPONENTS="$COMPONENTS status-dashboard service-registry"
|
|
|
|
|
|
log_info " Package: @packages/@ui/* -> all targets" >&2
|
2025-12-25 23:03:57 -08:00
|
|
|
|
fi
|
|
|
|
|
|
|
2025-12-25 23:28:34 -08:00
|
|
|
|
# Core packages affect all consumers
|
|
|
|
|
|
if echo "$CHANGED_FILES" | grep -q "^@packages/@core/"; then
|
|
|
|
|
|
COMPONENTS="$COMPONENTS status-dashboard service-registry"
|
|
|
|
|
|
log_info " Package: @packages/@core/* -> all targets" >&2
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# Deduplicate and return
|
|
|
|
|
|
echo "$COMPONENTS" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' '
|
|
|
|
|
|
fi
|
2025-12-25 23:03:57 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# DEPLOY COMPONENTS
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
deploy_component() {
|
|
|
|
|
|
local component="$1"
|
|
|
|
|
|
|
|
|
|
|
|
log_step "Deploying: $component"
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$DRY_RUN" = "--dry-run" ]; then
|
2025-12-26 05:49:18 -08:00
|
|
|
|
log_info "[DRY RUN] Would reconcile $component via reconciliation system"
|
2025-12-25 23:03:57 -08:00
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-12-26 05:49:18 -08:00
|
|
|
|
# All deployments now go through reconciliation system
|
2025-12-27 23:11:24 -08:00
|
|
|
|
local reconcile_dir="${PROJECT_ROOT}/infrastructure/reconciliation"
|
2025-12-26 05:49:18 -08:00
|
|
|
|
|
|
|
|
|
|
if [[ ! -x "$reconcile_dir/reconcile" ]]; then
|
|
|
|
|
|
log_error "Reconciliation system not found at: $reconcile_dir"
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-12-25 23:03:57 -08:00
|
|
|
|
case "$component" in
|
|
|
|
|
|
status-dashboard)
|
2025-12-26 05:49:18 -08:00
|
|
|
|
log_info "Running reconciliation for status-dashboard..."
|
|
|
|
|
|
"$reconcile_dir/reconcile" --host vps --service status-dashboard || {
|
|
|
|
|
|
log_warn "status-dashboard reconciliation failed (continuing)"
|
2025-12-25 23:03:57 -08:00
|
|
|
|
return 1
|
2025-12-26 05:49:18 -08:00
|
|
|
|
}
|
2025-12-25 23:03:57 -08:00
|
|
|
|
;;
|
|
|
|
|
|
service-registry)
|
2025-12-26 05:49:18 -08:00
|
|
|
|
log_info "Running reconciliation for service-registry..."
|
|
|
|
|
|
"$reconcile_dir/reconcile" --host vps --service service-registry || {
|
|
|
|
|
|
log_warn "service-registry reconciliation failed (continuing)"
|
2025-12-25 23:03:57 -08:00
|
|
|
|
return 1
|
2025-12-26 05:49:18 -08:00
|
|
|
|
}
|
2025-12-25 23:03:57 -08:00
|
|
|
|
;;
|
|
|
|
|
|
*)
|
2025-12-26 05:49:18 -08:00
|
|
|
|
log_warn "Unknown component: $component (no reconciliation handler)"
|
|
|
|
|
|
return 1
|
2025-12-25 23:03:57 -08:00
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
|
2025-12-26 05:49:18 -08:00
|
|
|
|
log_success "$component deployed via reconciliation"
|
2025-12-25 23:03:57 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# MAIN
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
main() {
|
|
|
|
|
|
log_header "Rectifier Auto-Deploy"
|
|
|
|
|
|
|
|
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
|
|
|
2025-12-27 23:11:24 -08:00
|
|
|
|
# Check SSL certificates first (before any deployment)
|
|
|
|
|
|
check_ssl_certificates || {
|
|
|
|
|
|
log_warn "SSL issues detected - continuing with deploy"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-25 23:03:57 -08:00
|
|
|
|
# Detect what changed
|
|
|
|
|
|
local CHANGED_COMPONENTS
|
|
|
|
|
|
CHANGED_COMPONENTS=$(detect_all_changes)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$CHANGED_COMPONENTS" ]; then
|
|
|
|
|
|
log_info "No deployable components changed"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
log_info "Components to deploy: $CHANGED_COMPONENTS"
|
|
|
|
|
|
|
|
|
|
|
|
# Deploy each component
|
|
|
|
|
|
local DEPLOY_COUNT=0
|
|
|
|
|
|
local FAIL_COUNT=0
|
|
|
|
|
|
|
|
|
|
|
|
for component in $CHANGED_COMPONENTS; do
|
|
|
|
|
|
if deploy_component "$component"; then
|
|
|
|
|
|
((DEPLOY_COUNT++)) || true
|
|
|
|
|
|
else
|
|
|
|
|
|
((FAIL_COUNT++)) || true
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
log_header "Rectifier Complete"
|
|
|
|
|
|
log_info "Deployed: $DEPLOY_COUNT component(s)"
|
|
|
|
|
|
if [ "$FAIL_COUNT" -gt 0 ]; then
|
|
|
|
|
|
log_warn "Failed: $FAIL_COUNT component(s)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main "$@"
|