2025-12-11 21:57:05 +00:00
|
|
|
|
# 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"]
|
chore: complete KCL to Nickel migration cleanup and setup pre-commit
Clean up 404 KCL references (99.75% complete):
- Rename kcl_* variables to schema_*/nickel_* (kcl_path→schema_path, etc.)
- Update functions: parse_kcl_file→parse_nickel_file
- Update env vars: KCL_MOD_PATH→NICKEL_IMPORT_PATH
- Fix cli/providers-install: add has_nickel and nickel_version variables
- Correct import syntax: .nickel.→.ncl.
- Update 57 files across core, CLI, config, and utilities
Configure pre-commit hooks:
- Activate: nushell-check, nickel-typecheck, markdownlint
- Comment out: Rust hooks (fmt, clippy, test), check-yaml
Testing:
- Module discovery: 9 modules (6 providers, 1 taskserv, 2 clusters) ✅
- Syntax validation: 15 core files ✅
- Pre-commit hooks: all passing ✅
2026-01-08 20:08:46 +00:00
|
|
|
|
# version = "3.0.0"
|
|
|
|
|
|
# requires = ["nushell:0.109.0"]
|
2025-12-11 21:57:05 +00:00
|
|
|
|
|
|
|
|
|
|
use ./mod.nu *
|
|
|
|
|
|
use ./detection.nu *
|
|
|
|
|
|
use ./validation.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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
def read-input-line [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
# Try to read from /dev/tty first (TTY/interactive mode)
|
2026-01-21 10:24:17 +00:00
|
|
|
|
let read_result = (do { open /dev/tty | lines | first | str trim } | complete)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
|
|
|
|
|
|
# If /dev/tty worked, return the line
|
2026-01-21 10:24:17 +00:00
|
|
|
|
if $read_result.exit_code == 0 {
|
|
|
|
|
|
($read_result.stdout)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
} 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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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 = ""
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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>
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# PROFILE SELECTION
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# Prompt for setup profile selection
|
|
|
|
|
|
export def prompt-profile-selection [] {
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print-setup-header "Profile Selection"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print "Choose a setup profile for your provisioning system:"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print " 1) Developer - Fast local setup (<5 min, Docker Compose, minimal config)"
|
|
|
|
|
|
print " 2) Production - Full validated setup (Kubernetes/SSH, complete security, HA)"
|
|
|
|
|
|
print " 3) CI/CD - Ephemeral pipeline setup (automated, Docker Compose, cleanup)"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
|
|
|
|
|
|
let options = ["Developer", "Production", "CI/CD"]
|
|
|
|
|
|
let choice = (prompt-select "Select profile" $options)
|
|
|
|
|
|
|
|
|
|
|
|
match $choice {
|
|
|
|
|
|
"Developer" => "developer"
|
|
|
|
|
|
"Production" => "production"
|
|
|
|
|
|
"CI/CD" => "cicd"
|
|
|
|
|
|
_ => "developer"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-11 21:57:05 +00:00
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# SYSTEM CONFIGURATION PROMPTS
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# Prompt for system configuration details
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def prompt-system-config [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def prompt-providers [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def prompt-security-config [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def prompt-initial-workspace [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
# Check if running in TTY or piped mode
|
2026-01-21 10:24:17 +00:00
|
|
|
|
let tty_check = (do { open /dev/tty | null } | complete)
|
|
|
|
|
|
let is_interactive = ($tty_check.exit_code == 0)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 2: Profile Selection (NEW - determines setup approach)
|
|
|
|
|
|
print ""
|
|
|
|
|
|
let profile = (prompt-profile-selection)
|
|
|
|
|
|
print-setup-success $"Selected profile: ($profile)"
|
|
|
|
|
|
|
|
|
|
|
|
# Step 3: System Configuration
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let system_config = (prompt-system-config)
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 5: Deployment Mode
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let deployment_mode = (prompt-deployment-mode $detection_report)
|
|
|
|
|
|
print-setup-success $"Selected deployment mode: ($deployment_mode)"
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 6: Provider Selection
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let providers = (prompt-providers)
|
|
|
|
|
|
print-setup-success $"Selected providers: ($providers | str join ', ')"
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 7: Resource Allocation
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let resources = (prompt-resource-allocation $detection_report)
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 8: Security Settings
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let security = (prompt-security-config)
|
|
|
|
|
|
|
2026-01-14 02:00:23 +00:00
|
|
|
|
# Step 9: Initial Workspace
|
2025-12-11 21:57:05 +00:00
|
|
|
|
let workspace = (prompt-initial-workspace)
|
|
|
|
|
|
|
|
|
|
|
|
# Summary
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print-setup-header "Setup Summary"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print "Configuration Details:"
|
2026-01-14 02:00:23 +00:00
|
|
|
|
print $" Profile: ($profile)"
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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
|
2026-01-14 02:00:23 +00:00
|
|
|
|
profile: ""
|
2025-12-11 21:57:05 +00:00
|
|
|
|
system_config: {}
|
|
|
|
|
|
deployment_mode: ""
|
|
|
|
|
|
providers: []
|
|
|
|
|
|
resources: {}
|
|
|
|
|
|
security: {}
|
|
|
|
|
|
workspace: {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print-setup-success "Configuration confirmed!"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
completed: true
|
2026-01-14 02:00:23 +00:00
|
|
|
|
profile: $profile
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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)
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def run-setup-with-defaults [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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)
|
2026-01-14 02:00:23 +00:00
|
|
|
|
export def run-minimal-setup [] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================================
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# TYPEDIALOG HELPER FUNCTIONS
|
2025-12-11 21:57:05 +00:00
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# Run TypeDialog form via bash wrapper and return parsed result
|
|
|
|
|
|
# This pattern avoids TTY/input issues in Nushell's execution stack
|
|
|
|
|
|
def run-typedialog-form [
|
|
|
|
|
|
wrapper_script: string
|
|
|
|
|
|
--backend: string = "tui"
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# Check if the wrapper script exists
|
|
|
|
|
|
if not ($wrapper_script | path exists) {
|
|
|
|
|
|
print-setup-warning "TypeDialog wrapper not found. Using fallback prompts."
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: false
|
|
|
|
|
|
error: "TypeDialog wrapper not available"
|
|
|
|
|
|
use_fallback: true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Set backend environment variable
|
|
|
|
|
|
$env.TYPEDIALOG_BACKEND = $backend
|
|
|
|
|
|
|
|
|
|
|
|
# Run bash wrapper (handles TTY input properly)
|
|
|
|
|
|
let result = (do { bash $wrapper_script } | complete)
|
|
|
|
|
|
|
|
|
|
|
|
if $result.exit_code != 0 {
|
|
|
|
|
|
print-setup-error "TypeDialog wizard failed or was cancelled"
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: false
|
|
|
|
|
|
error: $result.stderr
|
|
|
|
|
|
use_fallback: true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Read the generated JSON file
|
|
|
|
|
|
let json_output = ($wrapper_script | path dirname | path join "generated" | path join ($wrapper_script | path basename | str replace ".sh" "-result.json"))
|
|
|
|
|
|
|
|
|
|
|
|
if not ($json_output | path exists) {
|
|
|
|
|
|
print-setup-warning "TypeDialog output not found. Using fallback."
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: false
|
|
|
|
|
|
error: "Output file not found"
|
|
|
|
|
|
use_fallback: true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 10:24:17 +00:00
|
|
|
|
# Parse JSON output (no try-catch)
|
|
|
|
|
|
let parse_result = (do { open $json_output | from json } | complete)
|
|
|
|
|
|
if $parse_result.exit_code != 0 {
|
2026-01-09 15:08:49 +00:00
|
|
|
|
return {
|
|
|
|
|
|
success: false
|
|
|
|
|
|
error: "Failed to parse TypeDialog output"
|
|
|
|
|
|
use_fallback: true
|
|
|
|
|
|
}
|
2026-01-21 10:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let values = ($parse_result.stdout)
|
2026-01-09 15:08:49 +00:00
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
success: true
|
|
|
|
|
|
values: $values
|
|
|
|
|
|
use_fallback: false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# INTERACTIVE SETUP USING TYPEDIALOG
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# Run setup wizard using TypeDialog - modern TUI experience
|
|
|
|
|
|
# Uses bash wrapper to handle TTY input properly
|
|
|
|
|
|
export def run-setup-wizard-interactive [
|
|
|
|
|
|
--backend: string = "tui"
|
2026-01-14 02:00:23 +00:00
|
|
|
|
] {
|
2025-12-11 21:57:05 +00:00
|
|
|
|
print ""
|
|
|
|
|
|
print "╔═══════════════════════════════════════════════════════════════╗"
|
2026-01-09 15:08:49 +00:00
|
|
|
|
print "║ PROVISIONING SYSTEM SETUP WIZARD (TypeDialog) ║"
|
2025-12-11 21:57:05 +00:00
|
|
|
|
print "║ ║"
|
|
|
|
|
|
print "║ This wizard will guide you through setting up provisioning ║"
|
|
|
|
|
|
print "║ for your infrastructure automation needs. ║"
|
|
|
|
|
|
print "╚═══════════════════════════════════════════════════════════════╝"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# Run the TypeDialog-based wizard via bash wrapper
|
|
|
|
|
|
let wrapper_script = "provisioning/core/shlib/setup-wizard-tty.sh"
|
|
|
|
|
|
let form_result = (run-typedialog-form $wrapper_script --backend $backend)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# If TypeDialog not available or failed, fall back to basic wizard
|
|
|
|
|
|
if (not $form_result.success or $form_result.use_fallback) {
|
|
|
|
|
|
print-setup-info "Falling back to basic interactive wizard..."
|
|
|
|
|
|
return (run-setup-wizard)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Extract values from form results
|
|
|
|
|
|
let values = $form_result.values
|
|
|
|
|
|
|
|
|
|
|
|
# Collect selected providers
|
|
|
|
|
|
let providers = (
|
|
|
|
|
|
[]
|
2026-01-09 15:08:49 +00:00
|
|
|
|
| if ($values.providers?.upcloud? | default false) { append "upcloud" } else { . }
|
|
|
|
|
|
| if ($values.providers?.aws? | default false) { append "aws" } else { . }
|
|
|
|
|
|
| if ($values.providers?.hetzner? | default false) { append "hetzner" } else { . }
|
|
|
|
|
|
| if ($values.providers?.local? | default false) { append "local" } else { . }
|
2025-12-11 21:57:05 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure at least one provider
|
|
|
|
|
|
let providers_final = if ($providers | length) == 0 { ["local"] } else { $providers }
|
|
|
|
|
|
|
2026-01-09 15:08:49 +00:00
|
|
|
|
# Create workspace config
|
|
|
|
|
|
let workspace_final = {
|
|
|
|
|
|
create_workspace: ($values.workspace?.create_workspace? | default false)
|
|
|
|
|
|
name: ($values.workspace?.name? | default "default")
|
|
|
|
|
|
description: ($values.workspace?.description? | default "")
|
|
|
|
|
|
}
|
2025-12-11 21:57:05 +00:00
|
|
|
|
|
|
|
|
|
|
# Display summary
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print-setup-header "Setup Summary"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
print "Configuration Details:"
|
2026-01-09 15:08:49 +00:00
|
|
|
|
print $" Config Path: ($values.system_config?.config_path? | default (get-config-base-path))"
|
|
|
|
|
|
print $" Deployment Mode: ($values.deployment_mode? | default 'docker-compose')"
|
2025-12-11 21:57:05 +00:00
|
|
|
|
print $" Providers: ($providers_final | str join ', ')"
|
2026-01-09 15:08:49 +00:00
|
|
|
|
print $" CPUs: ($values.resources?.cpu_count? | default 4)"
|
|
|
|
|
|
print $" Memory: ($values.resources?.memory_gb? | default 8) GB"
|
|
|
|
|
|
print $" MFA Enabled: (if ($values.security?.enable_mfa? | default false) { 'Yes' } else { 'No' })"
|
|
|
|
|
|
print $" Audit Logging: (if ($values.security?.enable_audit? | default false) { 'Yes' } else { 'No' })"
|
2025-12-11 21:57:05 +00:00
|
|
|
|
print ""
|
|
|
|
|
|
|
|
|
|
|
|
print-setup-success "Configuration confirmed!"
|
|
|
|
|
|
print ""
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
completed: true
|
|
|
|
|
|
system_config: {
|
2026-01-09 15:08:49 +00:00
|
|
|
|
config_path: ($values.system_config?.config_path? | default (get-config-base-path))
|
2025-12-11 21:57:05 +00:00
|
|
|
|
os_name: (detect-os)
|
2026-01-09 15:08:49 +00:00
|
|
|
|
cpu_count: ($values.resources?.cpu_count? | default 4)
|
|
|
|
|
|
memory_gb: ($values.resources?.memory_gb? | default 8)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
}
|
2026-01-09 15:08:49 +00:00
|
|
|
|
deployment_mode: ($values.deployment_mode? | default "docker-compose")
|
2025-12-11 21:57:05 +00:00
|
|
|
|
providers: $providers_final
|
|
|
|
|
|
resources: {
|
2026-01-09 15:08:49 +00:00
|
|
|
|
cpu_count: ($values.resources?.cpu_count? | default 4)
|
|
|
|
|
|
memory_gb: ($values.resources?.memory_gb? | default 8)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
security: {
|
2026-01-09 15:08:49 +00:00
|
|
|
|
enable_mfa: ($values.security?.enable_mfa? | default true)
|
|
|
|
|
|
enable_audit: ($values.security?.enable_audit? | default true)
|
|
|
|
|
|
require_approval_for_destructive: ($values.security?.require_approval_for_destructive? | default true)
|
2025-12-11 21:57:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
workspace: $workspace_final
|
|
|
|
|
|
timestamp: (date now)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|