provisioning/scripts/setup-platform-config.sh

738 lines
22 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Platform Services Configuration Setup Script
# Generates and manages platform service configurations in provisioning/config/runtime/
#
# Usage: ./setup-platform-config.sh [options]
# ./setup-platform-config.sh --service orchestrator --mode solo
# ./setup-platform-config.sh --mode multiuser --backend web
# ./setup-platform-config.sh --list-modes
#
# Features:
# - Interactive TypeDialog configuration via provisioning/.typedialog/platform/scripts/configure.nu
# - Quick mode setup (solo, multiuser, cicd, enterprise) from defaults
# - Automatic TOML export for Rust service consumption
# - Runtime config detection and management (clean, update, preserve)
set -euo pipefail
2026-01-08 21:22:57 +00:00
# Ensure TypeDialog is installed
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/ensure-typedialog.sh" || true
# Check TypeDialog availability
if ! command -v typedialog &> /dev/null; then
echo -e "\033[1;33m⚠ TypeDialog not found. Attempting installation...\033[0m"
ensure_typedialog_installed true || {
echo -e "\033[0;31m❌ Failed to install TypeDialog\033[0m"
exit 1
}
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
TYPEDIALOG_SCRIPTS="${PROJECT_ROOT}/.typedialog/platform/scripts"
SCHEMAS_PLATFORM="${PROJECT_ROOT}/schemas/platform"
CONFIG_RUNTIME="${PROJECT_ROOT}/config/runtime"
CONFIG_GENERATED="${CONFIG_RUNTIME}/generated"
# Available services and modes
SERVICES=("orchestrator" "control-center" "mcp-server" "vault-service" "extension-registry" "rag" "ai-service" "provisioning-daemon")
MODES=("solo" "multiuser" "cicd" "enterprise")
# Default values
BACKEND="${BACKEND:-web}"
SERVICE=""
MODE=""
ACTION=""
FORCE=false
# ============================================================================
# Helper Functions
# ============================================================================
print_header() {
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}" >&2
}
# Check if directory/file exists
check_exists() {
if [[ ! -e "$1" ]]; then
print_error "Not found: $1"
return 1
fi
}
# ============================================================================
# Service/Mode Validation
# ============================================================================
validate_service() {
local service="$1"
# "all" is handled by bash script itself, not passed to Nushell
if [[ "$service" == "all" ]]; then
return 0
fi
if [[ ! " ${SERVICES[*]} " =~ " ${service} " ]]; then
print_error "Invalid service: $service"
echo "Valid services: ${SERVICES[*]}, all"
return 1
fi
}
validate_mode() {
local mode="$1"
if [[ ! " ${MODES[*]} " =~ " ${mode} " ]]; then
print_error "Invalid mode: $mode"
echo "Valid modes: ${MODES[*]}"
return 1
fi
}
# ============================================================================
# Directory Setup
# ============================================================================
ensure_runtime_dir() {
if [[ ! -d "$CONFIG_RUNTIME" ]]; then
print_info "Creating runtime config directory: $CONFIG_RUNTIME"
mkdir -p "$CONFIG_RUNTIME" "$CONFIG_GENERATED"
fi
# Create .gitignore if not present
if [[ ! -f "$CONFIG_RUNTIME/.gitignore" ]]; then
cat > "$CONFIG_RUNTIME/.gitignore" <<EOF
# Runtime configurations (private/deployment-specific)
*.ncl
*.toml
# Generated artifacts
generated/
# Backup files
*.bak
*.backup
EOF
print_info "Created .gitignore in $CONFIG_RUNTIME"
fi
}
# ============================================================================
# Config Detection & Management
# ============================================================================
detect_runtime_config() {
# Check if any runtime config exists
if ls "$CONFIG_RUNTIME"/*.ncl 1>/dev/null 2>&1; then
echo "existing"
return 0
fi
echo "empty"
return 0
}
list_runtime_services() {
local services=()
for file in "$CONFIG_RUNTIME"/*.ncl; do
if [[ -f "$file" ]]; then
local basename=$(basename "$file" .ncl)
services+=("$basename")
fi
done
if [[ ${#services[@]} -gt 0 ]]; then
printf '%s\n' "${services[@]}"
fi
}
# ============================================================================
# Interactive Prompts
# ============================================================================
prompt_action_existing_config() {
while true; do
echo ""
print_warning "Runtime configuration already exists in: $CONFIG_RUNTIME"
echo ""
echo "Choose action:"
echo " 1) Clean up and start fresh (removes all .ncl and .toml files)"
echo " 2) Use TypeDialog to update configuration [default]"
echo " 3) Setup quick mode (solo/multiuser/cicd/enterprise)"
echo " 4) List existing configurations"
echo " 5) Cancel"
echo ""
echo "Press CTRL-C to cancel at any time"
echo ""
read -rp "Enter choice [1-5] (default: 2): " choice
# Default to 2 (TypeDialog update)
choice="${choice:-2}"
case "$choice" in
1) ACTION="clean-start"; return 0 ;;
2) ACTION="typedialog"; return 0 ;;
3) ACTION="quick-mode"; return 0 ;;
4) ACTION="list"; return 0 ;;
5) print_info "Cancelled."; exit 0 ;;
*) print_error "Invalid choice. Please enter 1-5 (or press CTRL-C to abort)." ;;
esac
done
}
prompt_action_empty_config() {
while true; do
echo ""
echo "Choose how to setup platform configuration:"
echo " 1) Interactive TypeDialog (recommended, with UI form) [default]"
echo " 2) Quick mode setup (choose solo/multiuser/cicd/enterprise)"
echo " 3) Cancel"
echo ""
echo "Press CTRL-C to cancel at any time"
echo ""
read -rp "Enter choice [1-3] (default: 1): " choice
# Default to 1 if empty
choice="${choice:-1}"
case "$choice" in
1) ACTION="typedialog"; return 0 ;;
2) ACTION="quick-mode"; return 0 ;;
3) print_info "Cancelled."; exit 0 ;;
*) print_error "Invalid choice. Please enter 1, 2, or 3 (or press CTRL-C to abort)." ;;
esac
done
}
prompt_for_service() {
local max_choice=$((${#SERVICES[@]}+1))
while true; do
echo ""
echo "Select service to configure:"
for i in "${!SERVICES[@]}"; do
echo " $((i+1))) ${SERVICES[$i]}"
done
echo " $max_choice) Configure all services [default]"
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-$max_choice] (default: $max_choice): " choice
# Default to "all services"
choice="${choice:-$max_choice}"
# Validate numeric input
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
print_error "Invalid input. Please enter a number (or press CTRL-C to abort)."
continue
fi
if [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]]; then
if [[ "$choice" == "$max_choice" ]]; then
SERVICE="all"
else
SERVICE="${SERVICES[$((choice-1))]}"
fi
return 0
else
print_error "Invalid choice. Please enter a number between 1 and $max_choice (or press CTRL-C to abort)."
fi
done
}
prompt_for_mode() {
local max_choice=${#MODES[@]}
while true; do
echo ""
echo "Select deployment mode:"
for i in "${!MODES[@]}"; do
local marker=""
# Mark solo as default
if [[ "${MODES[$i]}" == "solo" ]]; then
marker=" [default]"
fi
echo " $((i+1))) ${MODES[$i]}$marker"
done
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-$max_choice] (default: 1): " choice
# Default to 1 (solo)
choice="${choice:-1}"
# Validate numeric input
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
print_error "Invalid input. Please enter a number (or press CTRL-C to abort)."
continue
fi
if [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]]; then
MODE="${MODES[$((choice-1))]}"
return 0
else
print_error "Invalid choice. Please enter a number between 1 and $max_choice (or press CTRL-C to abort)."
fi
done
}
prompt_for_backend() {
while true; do
echo ""
echo "Select TypeDialog backend:"
echo " 1) web (browser-based, recommended) [default]"
echo " 2) tui (terminal UI)"
echo " 3) cli (command-line)"
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-3] (default: 1): " choice
# Default to 1 (web)
choice="${choice:-1}"
case "$choice" in
1) BACKEND="web"; return 0 ;;
2) BACKEND="tui"; return 0 ;;
3) BACKEND="cli"; return 0 ;;
*) print_error "Invalid choice. Please enter 1, 2, or 3 (or press CTRL-C to abort)." ;;
esac
done
}
# ============================================================================
# Configuration Generation
# ============================================================================
generate_toml_for_service() {
local service="$1"
local mode="$2"
local ncl_file="${CONFIG_RUNTIME}/${service}.${mode}.ncl"
if [[ ! -f "$ncl_file" ]]; then
print_warning "Nickel config not found: $ncl_file"
return 1
fi
print_info "Exporting TOML for $service ($mode)..."
local toml_file="${CONFIG_GENERATED}/${service}.${mode}.toml"
# Generate TOML from Nickel
if nickel export --format toml "$ncl_file" > "$toml_file" 2>/dev/null; then
print_success "Generated: $toml_file"
return 0
else
print_error "Failed to generate TOML for $service ($mode)"
return 1
fi
}
generate_all_tomls() {
echo ""
print_header "Generating TOML Exports"
local generated_count=0
local failed_count=0
# Scan for all .ncl files in runtime
for ncl_file in "$CONFIG_RUNTIME"/*.ncl; do
if [[ -f "$ncl_file" ]]; then
local basename=$(basename "$ncl_file" .ncl)
local service="${basename%.*}" # Remove mode suffix
local mode="${basename##*.}" # Extract mode
if generate_toml_for_service "$service" "$mode"; then
((generated_count++))
else
((failed_count++))
fi
fi
done
echo ""
print_success "Generated $generated_count TOML files"
if [[ $failed_count -gt 0 ]]; then
print_warning "$failed_count files failed"
fi
}
# ============================================================================
# TypeDialog Configuration
# ============================================================================
configure_via_typedialog() {
check_exists "$TYPEDIALOG_SCRIPTS/configure.nu" || return 1
echo ""
print_header "TypeDialog Configuration"
# Prompt for service if not provided
if [[ -z "$SERVICE" ]]; then
prompt_for_service
else
validate_service "$SERVICE" || return 1
fi
# Prompt for mode if not provided
if [[ -z "$MODE" ]]; then
prompt_for_mode
else
validate_mode "$MODE" || return 1
fi
# Prompt for backend if using web
if [[ "$BACKEND" == "default" ]]; then
prompt_for_backend
fi
# Handle "all" services by iterating
if [[ "$SERVICE" == "all" ]]; then
print_info "Configuring all services for mode: $MODE"
echo ""
local success_count=0
local fail_count=0
for svc in "${SERVICES[@]}"; do
print_info "Launching TypeDialog ($BACKEND backend) for $svc ($MODE)..."
# Execute from scripts dir so relative module imports work
if (cd "$TYPEDIALOG_SCRIPTS" && nu configure.nu "$svc" "$MODE" --backend "$BACKEND"); then
print_success "Configuration completed for $svc"
generate_toml_for_service "$svc" "$MODE" && ((success_count++)) || ((fail_count++))
else
print_warning "Configuration skipped or failed for $svc"
((fail_count++))
fi
echo ""
done
print_info "Configured $success_count services, $fail_count failed/skipped"
return 0
fi
print_info "Launching TypeDialog ($BACKEND backend) for $SERVICE ($MODE)..."
echo ""
# Run TypeDialog via Nushell script (execute from scripts dir so module imports work)
if (cd "$TYPEDIALOG_SCRIPTS" && nu configure.nu "$SERVICE" "$MODE" --backend "$BACKEND"); then
print_success "Configuration completed for $SERVICE"
# Auto-generate TOML
generate_toml_for_service "$SERVICE" "$MODE"
print_info ""
print_info "Next steps:"
print_info " - Review config: cat $CONFIG_RUNTIME/$SERVICE.$MODE.ncl"
print_info " - Export TOML: $TYPEDIALOG_SCRIPTS/generate-configs.nu $SERVICE $MODE"
print_info " - Run service: ORCHESTRATOR_MODE=$MODE cargo run -p $SERVICE"
return 0
else
print_error "TypeDialog configuration cancelled or failed"
return 1
fi
}
# ============================================================================
# Quick Mode Setup (from defaults)
# ============================================================================
setup_quick_mode() {
echo ""
print_header "Quick Mode Setup"
# Prompt for mode if not provided
if [[ -z "$MODE" ]]; then
prompt_for_mode
else
validate_mode "$MODE" || return 1
fi
print_info "Setting up all services for mode: $MODE"
echo ""
local created_count=0
local failed_count=0
# Create config for each service by composing defaults + mode overlay
for service in "${SERVICES[@]}"; do
local ncl_file="$CONFIG_RUNTIME/${service}.${MODE}.ncl"
local schema_file="$SCHEMAS_PLATFORM/schemas/${service}.ncl"
local defaults_file="$SCHEMAS_PLATFORM/defaults/${service}-defaults.ncl"
local mode_defaults="$SCHEMAS_PLATFORM/defaults/deployment/${MODE}-defaults.ncl"
if [[ ! -f "$schema_file" ]] || [[ ! -f "$defaults_file" ]]; then
print_warning "Schema or defaults not found for $service (skipping)"
((failed_count++))
continue
fi
# Create Nickel config that composes: schema + defaults + mode overlay
cat > "$ncl_file" <<'EOF'
# Generated configuration for SERVICE (MODE mode)
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# DO NOT EDIT MANUALLY - Re-run setup-platform-config.sh to update
#
# This configuration is composed from:
# - Schema: provisioning/schemas/platform/schemas/SERVICE.ncl
# - Defaults: provisioning/schemas/platform/defaults/SERVICE-defaults.ncl
# - Mode: provisioning/schemas/platform/defaults/deployment/MODE-defaults.ncl
let helpers = import "provisioning/schemas/platform/common/helpers.ncl"
let schema = import "provisioning/schemas/platform/schemas/SERVICE.ncl"
let defaults = import "provisioning/schemas/platform/defaults/SERVICE-defaults.ncl"
let mode_config = import "provisioning/schemas/platform/defaults/deployment/MODE-defaults.ncl"
# Compose: base defaults + mode overlay + validators
helpers.compose_config defaults mode_config {}
EOF
# Replace placeholders with actual values
sed -i.bak "s|SERVICE|$service|g" "$ncl_file"
sed -i.bak "s|MODE|$MODE|g" "$ncl_file"
rm -f "${ncl_file}.bak"
if [[ -f "$ncl_file" ]]; then
print_success "Created config: $service ($MODE)"
# Generate TOML
if generate_toml_for_service "$service" "$MODE"; then
((created_count++))
else
((failed_count++))
fi
fi
done
echo ""
print_success "Setup complete: $created_count services configured"
if [[ $failed_count -gt 0 ]]; then
print_warning "$failed_count services failed"
fi
}
# ============================================================================
# Cleanup Operations
# ============================================================================
cleanup_runtime_config() {
echo ""
print_warning "This will delete all runtime configurations"
read -rp "Are you sure? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
print_info "Cancelled"
return 0
fi
print_info "Removing runtime configurations..."
rm -f "$CONFIG_RUNTIME"/*.ncl "$CONFIG_GENERATED"/*.toml
print_success "Cleanup complete"
}
# ============================================================================
# Listing Operations
# ============================================================================
list_deployment_modes() {
echo ""
print_info "Available deployment modes:"
for mode in "${MODES[@]}"; do
echo " - $mode"
done
}
list_available_services() {
echo ""
print_info "Available services:"
for service in "${SERVICES[@]}"; do
echo " - $service"
done
}
list_runtime_configs() {
echo ""
print_header "Runtime Configurations"
local status=$(detect_runtime_config)
if [[ "$status" == "empty" ]]; then
print_info "No runtime configurations found"
return 0
fi
echo "Configured services:"
list_runtime_services | while read -r service; do
echo "$service"
done
echo ""
echo "Location: $CONFIG_RUNTIME"
}
# ============================================================================
# Help Text
# ============================================================================
show_help() {
cat <<EOF
${BLUE}Platform Services Configuration Setup${NC}
Usage: $(basename "$0") [OPTIONS]
Options:
-s, --service SERVICE Configure specific service (orchestrator, control-center, etc.)
-m, --mode MODE Set deployment mode (solo, multiuser, cicd, enterprise)
-b, --backend BACKEND TypeDialog backend: web (default), tui, cli
--typedialog Use TypeDialog for configuration
--quick-mode Use quick mode setup from defaults
--list-modes List available deployment modes
--list-services List available services
--list-configs List existing runtime configurations
--clean Remove all runtime configurations
--generate-toml Generate TOML files from Nickel configs
-h, --help Show this help message
Examples:
# Interactive setup (prompts for choices)
$(basename "$0")
# Configure specific service via TypeDialog
$(basename "$0") --service orchestrator --mode solo --backend web
# Quick setup for all services in solo mode
$(basename "$0") --quick-mode --mode solo
# List available modes
$(basename "$0") --list-modes
# Regenerate TOML files
$(basename "$0") --generate-toml
Environment Variables:
TYPEDIALOG_BACKEND Override TypeDialog backend (web, tui, cli)
EOF
}
# ============================================================================
# Main Entry Point
# ============================================================================
main() {
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-s|--service) SERVICE="$2"; shift 2 ;;
-m|--mode) MODE="$2"; shift 2 ;;
-b|--backend) BACKEND="$2"; shift 2 ;;
--typedialog) ACTION="typedialog"; shift ;;
--quick-mode) ACTION="quick-mode"; shift ;;
--list-modes) ACTION="list-modes"; shift ;;
--list-services) ACTION="list-services"; shift ;;
--list-configs) ACTION="list"; shift ;;
--clean) ACTION="clean"; shift ;;
--generate-toml) ACTION="generate-toml"; shift ;;
--force) FORCE=true; shift ;;
-h|--help) show_help; exit 0 ;;
*) print_error "Unknown option: $1"; show_help; exit 1 ;;
esac
done
# Ensure directories exist
ensure_runtime_dir
# Check for required tools
if ! command -v nickel &>/dev/null; then
print_error "Nickel not found. Please install Nickel (https://nickel-lang.org/)"
exit 1
fi
if ! command -v nu &>/dev/null; then
print_error "Nushell not found. Please install Nushell"
exit 1
fi
# Execute requested action
case "${ACTION:-}" in
typedialog)
configure_via_typedialog || exit 1
;;
quick-mode)
setup_quick_mode || exit 1
;;
clean)
cleanup_runtime_config
;;
list)
list_runtime_configs
;;
list-modes)
list_deployment_modes
;;
list-services)
list_available_services
;;
generate-toml)
generate_all_tomls
;;
*)
# No action specified - interactive mode
print_header "Platform Services Configuration Setup"
local runtime_status=$(detect_runtime_config)
if [[ "$runtime_status" == "existing" ]]; then
prompt_action_existing_config
else
prompt_action_empty_config
fi
# Execute chosen action
case "$ACTION" in
typedialog) configure_via_typedialog || exit 1 ;;
quick-mode) setup_quick_mode || exit 1 ;;
clean) cleanup_runtime_config ;;
list) list_runtime_configs ;;
*) print_error "No action selected"; exit 1 ;;
esac
;;
esac
echo ""
print_success "Done!"
}
# Run main function
main "$@"