642 lines
20 KiB
Plaintext
Raw Permalink Normal View History

# Interactive Setup Wizard Module
# Provides step-by-step interactive guidance for system setup
# Follows Nushell guidelines: explicit types, single purpose, no try-catch
# [command]
# name = "setup wizard"
# group = "configuration"
# tags = ["setup", "interactive", "wizard"]
# version = "2.0.0"
# requires = ["forminquire.nu:1.0.0", "nushell:0.109.0"]
# note = "Migrated to FormInquire with fallback to prompt-* functions"
use ./mod.nu *
use ./detection.nu *
use ./validation.nu *
use ../../forminquire/nulib/forminquire.nu *
# ============================================================================
# INPUT HELPERS
# ============================================================================
# Helper to read one line of input in Nushell 0.109.1
# Reads directly from /dev/tty for TTY mode, handles piped input gracefully
def read-input-line []: string -> string {
# Try to read from /dev/tty first (TTY/interactive mode)
let tty_result = (try {
open /dev/tty | lines | first | str trim
} catch {
null
})
# If /dev/tty worked, return the line
if $tty_result != null {
$tty_result
} else {
# No /dev/tty (Windows, containers, or piped mode)
# Return empty string - this will use defaults in calling code
""
}
}
# Prompt user for simple yes/no question
export def prompt-yes-no [
question: string
]: nothing -> bool {
print ""
print -n ($question + " (y/n): ")
let response = (read-input-line)
if ($response | is-empty) { false } else { ($response == "y" or $response == "Y") }
}
# Prompt user for text input
export def prompt-text [
question: string
default_value: string = ""
]: nothing -> string {
print ""
if ($default_value != "") {
print ($question + " [" + $default_value + "]: ")
} else {
print ($question + ": ")
}
let response = (read-input-line)
if ($response == "") {
$default_value
} else {
$response
}
}
# Prompt user for selection from list
export def prompt-select [
question: string
options: list<string>
]: nothing -> string {
print ""
print $question
let option_count = ($options | length)
for i in (0..($option_count - 1)) {
let option = ($options | get $i)
let num = (($i + 1) | into string)
print (" " + $num + ") " + $option)
}
print ""
let count_str = ($option_count | into string)
print ("Select option (1-" + $count_str + "): ")
let choice_str = (read-input-line)
let choice = ($choice_str | into int | default 1)
if ($choice >= 1 and $choice <= $option_count) {
$options | get ($choice - 1)
} else {
$options | get 0
}
}
# Prompt user for number with validation
export def prompt-number [
question: string
min_value: int = 1
max_value: int = 1000
default_value: int = 0
]: nothing -> int {
mut result = $default_value
mut valid = false
while (not $valid) {
print ""
if ($default_value != 0) {
print $"$question [$default_value]: "
} else {
print $"$question: "
}
let input_value = (read-input-line)
if ($input_value == "") {
$result = $default_value
$valid = true
} else {
let parsed = (do { $input_value | into int } | complete)
if ($parsed.exit_code == 0) {
let num = ($parsed.stdout | str trim | into int)
if ($num >= $min_value and $num <= $max_value) {
$result = $num
$valid = true
} else {
print-setup-warning $"Please enter a number between ($min_value) and ($max_value)"
}
} else {
print-setup-warning "Please enter a valid number"
}
}
}
$result
}
# ============================================================================
# SYSTEM CONFIGURATION PROMPTS
# ============================================================================
# Prompt for system configuration details
export def prompt-system-config []: nothing -> record {
print-setup-header "System Configuration"
print ""
print "Let's configure your provisioning system. This will set up the base configuration."
print ""
let config_path = (get-config-base-path)
print-setup-info $"Configuration will be stored in: ($config_path)"
let use_defaults = (prompt-yes-no "Use recommended paths for your OS?")
let confirmed_path = if $use_defaults {
config_path
} else {
(prompt-text "Configuration base path" $config_path)
}
{
config_path: $confirmed_path
os_name: (detect-os)
cpu_count: (get-cpu-count)
memory_gb: (get-system-memory-gb)
}
}
# ============================================================================
# DEPLOYMENT MODE PROMPTS
# ============================================================================
# Prompt for deployment mode selection
export def prompt-deployment-mode [
detection_report: record
]: nothing -> string {
print-setup-header "Deployment Mode Selection"
print ""
print "Choose how platform services will be deployed:"
print ""
mut options = []
let caps = $detection_report.capabilities
if ($caps.docker_available and $caps.docker_compose_available) {
$options = ($options | append "docker-compose (Local Docker)")
}
if $caps.kubectl_available {
$options = ($options | append "kubernetes (Kubernetes cluster)")
}
if $caps.ssh_available {
$options = ($options | append "remote-ssh (Remote SSH)")
}
if $caps.systemd_available {
$options = ($options | append "systemd (System services)")
}
if ($options | length) == 0 {
print-setup-error "No deployment methods available"
return "unknown"
}
let recommended = (recommend-deployment-mode $detection_report)
print ("Recommended: " + $recommended)
print ""
let selected = (prompt-select "Select deployment mode" $options)
match $selected {
"docker-compose (Local Docker)" => { "docker-compose" }
"kubernetes (Kubernetes cluster)" => { "kubernetes" }
"remote-ssh (Remote SSH)" => { "remote-ssh" }
"systemd (System services)" => { "systemd" }
_ => { "docker-compose" }
}
}
# ============================================================================
# PROVIDER CONFIGURATION PROMPTS
# ============================================================================
# Prompt for provider selection
export def prompt-providers []: nothing -> list<string> {
print-setup-header "Provider Selection"
print ""
print "Which infrastructure providers do you want to use?"
print "(Select at least one)"
print ""
let available_providers = ["upcloud", "aws", "hetzner", "local"]
mut selected = []
for provider in $available_providers {
let use_provider = (prompt-yes-no $"Use ($provider)?")
if $use_provider {
$selected = ($selected | append $provider)
}
}
if ($selected | length) == 0 {
print-setup-info "At least 'local' provider is required"
["local"]
} else {
$selected
}
}
# ============================================================================
# RESOURCE CONFIGURATION PROMPTS
# ============================================================================
# Prompt for resource allocation
export def prompt-resource-allocation [
detection_report: record
]: nothing -> record {
print-setup-header "Resource Allocation"
print ""
let current_cpus = $detection_report.system.cpu_count
let current_memory = $detection_report.system.memory_gb
let cpu_count = (prompt-number "Number of CPUs to allocate" 1 $current_cpus $current_cpus)
let memory_gb = (prompt-number "Memory in GB to allocate" 1 $current_memory $current_memory)
print ""
print $"✅ Allocated ($cpu_count) CPUs and ($memory_gb) GB memory"
{
cpu_count: $cpu_count
memory_gb: $memory_gb
}
}
# ============================================================================
# SECURITY CONFIGURATION PROMPTS
# ============================================================================
# Prompt for security settings
export def prompt-security-config []: nothing -> record {
print-setup-header "Security Configuration"
print ""
let enable_mfa = (prompt-yes-no "Enable Multi-Factor Authentication (MFA)?")
let enable_audit = (prompt-yes-no "Enable audit logging for all operations?")
let require_approval = (prompt-yes-no "Require approval for destructive operations?")
{
enable_mfa: $enable_mfa
enable_audit: $enable_audit
require_approval_for_destructive: $require_approval
}
}
# ============================================================================
# WORKSPACE CONFIGURATION PROMPTS
# ============================================================================
# Prompt for initial workspace creation
export def prompt-initial-workspace []: nothing -> record {
print-setup-header "Initial Workspace"
print ""
print "Create an initial workspace for your infrastructure?"
print ""
let create_workspace = (prompt-yes-no "Create workspace now?")
if not $create_workspace {
return {
create_workspace: false
name: ""
description: ""
}
}
let workspace_name = (prompt-text "Workspace name" "default")
let workspace_description = (prompt-text "Workspace description (optional)" "")
{
create_workspace: true
name: $workspace_name
description: $workspace_description
}
}
# ============================================================================
# COMPLETE SETUP WIZARD
# ============================================================================
# Run complete interactive setup wizard
export def run-setup-wizard [
--verbose = false
]: nothing -> record {
# Check if running in TTY or piped mode
let is_interactive = (try {
open /dev/tty | null
true
} catch {
false
})
if not $is_interactive {
# In non-TTY mode, switch to defaults automatically
print " Non-interactive mode detected. Using recommended defaults..."
print ""
return (run-setup-with-defaults)
}
# Force immediate output
print ""
print "╔═══════════════════════════════════════════════════════════════╗"
print "║ PROVISIONING SYSTEM SETUP WIZARD ║"
print "║ ║"
print "║ This wizard will guide you through setting up provisioning ║"
print "║ for your infrastructure automation needs. ║"
print "╚═══════════════════════════════════════════════════════════════╝"
print ""
# Step 1: Environment Detection
print-setup-header "Step 1: Environment Detection"
print "Analyzing your system configuration..."
print ""
# Use simplified detection to avoid hangs
let detection_report = {
system: {
os: (detect-os)
architecture: (detect-architecture)
hostname: "localhost"
current_user: (get-current-user)
cpu_count: 4
memory_gb: 8
disk_gb: 100
}
capabilities: (get-deployment-capabilities)
network: {
internet_connected: true
docker_port_available: true
orchestrator_port_available: true
control_center_port_available: true
kms_port_available: true
}
existing_config: (get-existing-config-summary)
platform_services: {
orchestrator_running: false
control_center_running: false
kms_running: false
}
timestamp: (date now)
}
if $verbose {
print-detection-report $detection_report
}
# Step 2: System Configuration
let system_config = (prompt-system-config)
# Step 3: Deployment Mode
let deployment_mode = (prompt-deployment-mode $detection_report)
print-setup-success $"Selected deployment mode: ($deployment_mode)"
# Step 4: Provider Selection
let providers = (prompt-providers)
print-setup-success $"Selected providers: ($providers | str join ', ')"
# Step 5: Resource Allocation
let resources = (prompt-resource-allocation $detection_report)
# Step 6: Security Settings
let security = (prompt-security-config)
# Step 7: Initial Workspace
let workspace = (prompt-initial-workspace)
# Summary
print ""
print-setup-header "Setup Summary"
print ""
print "Configuration Details:"
print $" Config Path: ($system_config.config_path)"
print $" OS: ($system_config.os_name)"
print $" Deployment Mode: ($deployment_mode)"
print $" Providers: ($providers | str join ', ')"
print $" CPUs: ($resources.cpu_count)"
print $" Memory: ($resources.memory_gb) GB"
print $" MFA Enabled: (if $security.enable_mfa { 'Yes' } else { 'No' })"
print $" Audit Logging: (if $security.enable_audit { 'Yes' } else { 'No' })"
print ""
let confirm = (prompt-yes-no "Proceed with this configuration?")
if not $confirm {
print-setup-warning "Setup cancelled"
return {
completed: false
system_config: {}
deployment_mode: ""
providers: []
resources: {}
security: {}
workspace: {}
}
}
print ""
print-setup-success "Configuration confirmed!"
print ""
{
completed: true
system_config: $system_config
deployment_mode: $deployment_mode
providers: $providers
resources: $resources
security: $security
workspace: $workspace
timestamp: (date now)
}
}
# ============================================================================
# QUICK SETUP (AUTOMATED WITH DEFAULTS)
# ============================================================================
# Run setup with recommended defaults (no interaction)
export def run-setup-with-defaults []: nothing -> record {
print-setup-header "Quick Setup (Recommended Defaults)"
print ""
print "Configuring with system-recommended defaults..."
print ""
{
completed: true
system_config: {
config_path: (get-config-base-path)
os_name: (detect-os)
cpu_count: (get-cpu-count)
memory_gb: (get-system-memory-gb)
}
deployment_mode: "docker-compose"
providers: ["local"]
resources: {
cpu_count: (get-cpu-count)
memory_gb: (get-system-memory-gb)
}
security: {
enable_mfa: true
enable_audit: true
require_approval_for_destructive: true
}
workspace: {
create_workspace: true
name: "default"
description: "Default workspace"
}
timestamp: (date now)
}
}
# Run minimal setup (only required settings)
export def run-minimal-setup []: nothing -> record {
print-setup-header "Minimal Setup"
print ""
print "Configuring with minimal required settings..."
print ""
{
completed: true
system_config: {
config_path: (get-config-base-path)
os_name: (detect-os)
}
deployment_mode: "docker-compose"
providers: ["local"]
resources: {}
security: {
enable_mfa: false
enable_audit: true
require_approval_for_destructive: false
}
workspace: {
create_workspace: false
}
timestamp: (date now)
}
}
# ============================================================================
# INTERACTIVE SETUP USING FORMINQUIRE (NEW)
# ============================================================================
# Run setup wizard using FormInquire - modern TUI experience
export def run-setup-wizard-interactive []: nothing -> record {
print ""
print "╔═══════════════════════════════════════════════════════════════╗"
print "║ PROVISIONING SYSTEM SETUP WIZARD (FormInquire) ║"
print "║ ║"
print "║ This wizard will guide you through setting up provisioning ║"
print "║ for your infrastructure automation needs. ║"
print "╚═══════════════════════════════════════════════════════════════╝"
print ""
# Prepare context with system information for form defaults
let context = {
config_path: (get-config-base-path)
cpu_count: (get-cpu-count)
memory_gb: (get-system-memory-gb)
disk_gb: 100
}
# Run the FormInquire-based wizard
let form_result = (setup-wizard-form)
if not $form_result.success {
print-setup-warning "Setup cancelled or failed"
return {
completed: false
system_config: {}
deployment_mode: ""
providers: []
resources: {}
security: {}
workspace: {}
}
}
# Extract values from form results
let values = $form_result.values
# Collect selected providers
let providers = (
[]
| if ($values.provider_upcloud? | default false) { append "upcloud" } else { . }
| if ($values.provider_aws? | default false) { append "aws" } else { . }
| if ($values.provider_hetzner? | default false) { append "hetzner" } else { . }
| if ($values.provider_local? | default false) { append "local" } else { . }
)
# Ensure at least one provider
let providers_final = if ($providers | length) == 0 { ["local"] } else { $providers }
# Create workspace config if requested
let workspace_final = (
if ($values.create_workspace? | default false) {
{
create_workspace: true
name: ($values.workspace_name? | default "default")
description: ($values.workspace_description? | default "")
}
} else {
{
create_workspace: false
}
}
)
# Display summary
print ""
print-setup-header "Setup Summary"
print ""
print "Configuration Details:"
print $" Config Path: ($values.config_path)"
print $" Deployment Mode: ($values.deployment_mode)"
print $" Providers: ($providers_final | str join ', ')"
print $" CPUs: ($values.cpu_count)"
print $" Memory: ($values.memory_gb) GB"
print $" Disk: ($values.disk_gb) GB"
print $" MFA Enabled: (if ($values.enable_mfa? | default false) { 'Yes' } else { 'No' })"
print $" Audit Logging: (if ($values.enable_audit_logging? | default false) { 'Yes' } else { 'No' })"
print ""
print-setup-success "Configuration confirmed!"
print ""
{
completed: true
system_config: {
config_path: ($values.config_path)
os_name: (detect-os)
cpu_count: ($values.cpu_count | into int)
memory_gb: ($values.memory_gb | into int)
}
deployment_mode: ($values.deployment_mode)
providers: $providers_final
resources: {
cpu_count: ($values.cpu_count | into int)
memory_gb: ($values.memory_gb | into int)
disk_gb: ($values.disk_gb | into int)
}
security: {
enable_mfa: ($values.enable_mfa? | default false)
enable_audit: ($values.enable_audit_logging? | default false)
require_approval_for_destructive: ($values.require_approval? | default false)
}
workspace: $workspace_final
timestamp: (date now)
}
}