Jesús Pérez 85ce530733
feat: update provisioning core CLI, libraries, and plugins
Update core components including CLI, Nushell libraries, plugins system,
and utility scripts for the provisioning system.

CLI Updates:
- Command implementations
- CLI utilities and dispatching
- Help system improvements
- Command validation

Library Updates:
- Configuration management system
- Infrastructure validation
- Extension system improvements
- Secrets management
- Workspace operations
- Cache management system

Plugin System:
- Interactive form plugin (inquire)
- KCL integration plugin
- Performance optimization plugins
- Plugin registration system

Utilities:
- Build and distribution scripts
- Installation procedures
- Testing utilities
- Development tools

Documentation:
- Library module documentation
- Extension API guides
- Plugin usage guides
- Service management documentation

All changes are backward compatible. No breaking changes.
2025-12-11 21:57:05 +00:00

642 lines
20 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
}
}