1112 lines
37 KiB
Plaintext
Raw Normal View History

2025-10-07 10:32:04 +01:00
# Utility Command Handlers
# Handles: ssh, sed, sops, cache, providers, nu, list, qr
use ../flags.nu *
use ../../lib_provisioning *
use ../../servers/ssh.nu *
use ../../servers/utils.nu *
# Helper to run module commands
def run_module [
args: string
module: string
option?: string
--exec
] {
let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
if $exec {
exec $"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args
} else {
^$"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args
}
}
# Main utility command dispatcher
export def handle_utility_command [
command: string
ops: string
flags: record
] {
match $command {
"ssh" => { handle_ssh $flags }
"sed" | "sops" => { handle_sops_edit $command $ops $flags }
"cache" => { handle_cache $ops $flags }
"providers" => { handle_providers $ops $flags }
2025-10-07 10:32:04 +01:00
"nu" => { handle_nu $ops $flags }
"list" | "l" | "ls" => { handle_list $ops $flags }
"qr" => { handle_qr }
"nuinfo" => { handle_nuinfo }
"plugin" | "plugins" => { handle_plugins $ops $flags }
"guide" | "guides" | "howto" => { handle_guide $ops $flags }
2025-10-07 10:32:04 +01:00
_ => {
print $"❌ Unknown utility command: ($command)"
print ""
print "Available utility commands:"
print " ssh - SSH into server"
print " sed - Edit SOPS encrypted files (alias)"
print " sops - Edit SOPS encrypted files"
print " cache - Cache management (status, config, clear, list)"
print " providers - List available providers"
print " nu - Start Nushell with provisioning library loaded"
print " list - List resources (servers, taskservs, clusters)"
print " qr - Generate QR code"
print " nuinfo - Show Nushell version info"
print " plugin - Plugin management (list, register, test, status)"
print " guide - Show interactive guides (from-scratch, update, customize)"
print ""
print "Use 'provisioning help utilities' for more details"
2025-10-07 10:32:04 +01:00
exit 1
}
}
}
# SSH command handler
def handle_ssh [flags: record] {
let curr_settings = (find_get_settings --infra $flags.infra --settings $flags.settings $flags.include_notuse)
rm -rf $curr_settings.wk_path
server_ssh $curr_settings "" "pub" false
}
# SOPS edit command handler
def handle_sops_edit [task: string, ops: string, flags: record] {
let pos = if $task == "sed" { 0 } else { 1 }
let ops_parts = ($ops | split row " ")
let target_file = if ($ops_parts | length) > $pos { $ops_parts | get $pos } else { "" }
2025-10-07 10:32:04 +01:00
if ($target_file | is-empty) {
throw-error $"🛑 No file found" $"for (_ansi yellow_bold)sops(_ansi reset) edit"
exit -1
}
let target_full_path = if not ($target_file | path exists) {
let infra_path = (get_infra $flags.infra)
let candidate = ($infra_path | path join $target_file)
if ($candidate | path exists) {
$candidate
} else {
throw-error $"🛑 No file (_ansi green_italic)($target_file)(_ansi reset) found" $"for (_ansi yellow_bold)sops(_ansi reset) edit"
exit -1
}
} else {
$target_file
}
# Setup SOPS environment if needed
if ($env.PROVISIONING_SOPS? | is-empty) {
let curr_settings = (find_get_settings --infra $flags.infra --settings $flags.settings $flags.include_notuse)
rm -rf $curr_settings.wk_path
$env.CURRENT_INFRA_PATH = ($curr_settings.infra_path | path join $curr_settings.infra)
use ../../sops_env.nu
}
if $task == "sed" {
on_sops "sed" $target_full_path
} else {
on_sops $task $target_full_path ($ops_parts | skip 1)
}
}
# Cache command handler
def handle_cache [ops: string, flags: record] {
use ../../lib_provisioning/config/cache/simple-cache.nu *
# Parse cache subcommand
let parts = if ($ops | is-not-empty) {
($ops | str trim | split row " " | where { |x| ($x | is-not-empty) })
} else {
[]
}
let subcommand = if ($parts | length) > 0 { $parts | get 0 } else { "status" }
let args = if ($parts | length) > 1 { $parts | skip 1 } else { [] }
# Handle cache commands
match $subcommand {
"status" => {
print ""
cache-status
print ""
}
"config" => {
let config_cmd = if ($args | length) > 0 { $args | get 0 } else { "show" }
match $config_cmd {
"show" => {
print ""
let config = (get-cache-config)
let cache_base = (($env.HOME? | default "~" | path expand) | path join ".provisioning" "cache" "config")
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print "📋 Cache Configuration"
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print ""
print "▸ Core Settings:"
let enabled = ($config | get --optional enabled | default true)
print (" Enabled: " + ($enabled | into string))
print ""
print "▸ Cache Location:"
print (" Base Path: " + $cache_base)
print ""
print "▸ Time-To-Live (TTL) Settings:"
let ttl_final = ($config | get --optional ttl_final_config | default "300")
let ttl_kcl = ($config | get --optional ttl_kcl | default "1800")
let ttl_sops = ($config | get --optional ttl_sops | default "900")
print (" Final Config: " + ($ttl_final | into string) + "s (5 minutes)")
print (" KCL Compilation: " + ($ttl_kcl | into string) + "s (30 minutes)")
print (" SOPS Decryption: " + ($ttl_sops | into string) + "s (15 minutes)")
print " Provider Config: 600s (10 minutes)"
print " Platform Config: 600s (10 minutes)"
print ""
print "▸ Security Settings:"
print " SOPS File Permissions: 0600 (owner read-only)"
print " SOPS Directory Permissions: 0700 (owner access only)"
print ""
print "▸ Validation Settings:"
print " Strict mtime Checking: true (validates all source files)"
print ""
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print ""
}
"get" => {
if ($args | length) > 1 {
let setting = $args | get 1
let value = (cache-config-get $setting)
if $value != null {
print $"($setting) = ($value)"
} else {
print $"Setting not found: ($setting)"
}
} else {
print "❌ cache config get requires a setting path"
print "Usage: provisioning cache config get <path>"
exit 1
}
}
"set" => {
if ($args | length) > 2 {
let setting = $args | get 1
let value = ($args | skip 2 | str join " ")
cache-config-set $setting $value
print $"✓ Set ($setting) = ($value)"
} else {
print "❌ cache config set requires setting path and value"
print "Usage: provisioning cache config set <path> <value>"
exit 1
}
}
_ => {
print $"❌ Unknown cache config subcommand: ($config_cmd)"
print ""
print "Available cache config subcommands:"
print " show - Show all cache configuration"
print " get <setting> - Get specific cache setting"
print " set <key> <val> - Set cache setting"
print ""
print "Available settings for get/set:"
print " enabled - Cache enabled (true/false)"
print " ttl_final_config - TTL for final config (seconds)"
print " ttl_kcl - TTL for KCL compilation (seconds)"
print " ttl_sops - TTL for SOPS decryption (seconds)"
print ""
print "Examples:"
print " provisioning cache config show"
print " provisioning cache config get ttl_final_config"
print " provisioning cache config set ttl_final_config 600"
exit 1
}
}
}
"clear" => {
let cache_type = if ($args | length) > 0 { $args | get 0 } else { "all" }
cache-clear $cache_type
print $"✓ Cleared cache: ($cache_type)"
}
"list" => {
let cache_type = if ($args | length) > 0 { $args | get 0 } else { "*" }
let items = (cache-list $cache_type)
if ($items | length) > 0 {
print $"Cache items \(type: ($cache_type)\):"
$items | each { |item| print $" ($item)" }
} else {
print "No cache items found"
}
}
"help" => {
print "
Cache Management Commands:
provisioning cache status # Show cache status and statistics
provisioning cache config show # Show cache configuration
provisioning cache config get <setting> # Get specific cache setting
provisioning cache config set <setting> <val> # Set cache setting
provisioning cache clear [type] # Clear cache (default: all)
provisioning cache list [type] # List cached items (default: all)
provisioning cache help # Show this help message
Available settings (for get/set):
enabled - Cache enabled (true/false)
ttl_final_config - TTL for final config (seconds)
ttl_kcl - TTL for KCL compilation (seconds)
ttl_sops - TTL for SOPS decryption (seconds)
Examples:
provisioning cache status
provisioning cache config get ttl_final_config
provisioning cache config set ttl_final_config 600
provisioning cache config set enabled false
provisioning cache clear kcl
provisioning cache list
"
}
_ => {
print $"❌ Unknown cache command: ($subcommand)"
print ""
print "Available cache commands:"
print " status - Show cache status and statistics"
print " config show - Show cache configuration"
print " config get <key> - Get specific cache setting"
print " config set <k> <v> - Set cache setting"
print " clear [type] - Clear cache (all, kcl, sops, final)"
print " list [type] - List cached items"
print " help - Show this help message"
print ""
print "Examples:"
print " provisioning cache status"
print " provisioning cache config get ttl_final_config"
print " provisioning cache config set ttl_final_config 600"
print " provisioning cache clear kcl"
exit 1
}
}
2025-10-07 10:32:04 +01:00
}
# Providers command handler - supports list, info, install, remove, installed, validate
def handle_providers [ops: string, flags: record] {
use ../../lib_provisioning/kcl_module_loader.nu *
# Parse subcommand and arguments
let parts = if ($ops | is-not-empty) {
($ops | str trim | split row " " | where { |x| ($x | is-not-empty) })
} else {
[]
}
let subcommand = if ($parts | length) > 0 { $parts | get 0 } else { "list" }
let args = if ($parts | length) > 1 { $parts | skip 1 } else { [] }
match $subcommand {
"list" => { handle_providers_list $flags $args }
"info" => { handle_providers_info $args $flags }
"install" => { handle_providers_install $args $flags }
"remove" => { handle_providers_remove $args $flags }
"installed" => { handle_providers_installed $args $flags }
"validate" => { handle_providers_validate $args $flags }
"help" | "-h" | "--help" => { show_providers_help }
_ => {
print $"❌ Unknown providers subcommand: ($subcommand)"
print ""
show_providers_help
exit 1
}
}
}
# List all available providers
def handle_providers_list [flags: record, args: list] {
use ../../lib_provisioning/kcl_module_loader.nu *
2025-10-07 10:32:04 +01:00
_print $"(_ansi green)PROVIDERS(_ansi reset) list: \n"
# Parse flags
let show_kcl = ($args | any { |x| $x == "--kcl" })
let format_idx = ($args | enumerate | where item == "--format" | get 0?.index | default (-1))
let format = if $format_idx >= 0 and ($args | length) > ($format_idx + 1) {
$args | get ($format_idx + 1)
} else {
"table"
}
let no_cache = ($args | any { |x| $x == "--no-cache" })
# Get providers using cached KCL module loader
let providers = if $no_cache {
(discover-kcl-modules "providers")
} else {
(discover-kcl-modules-cached "providers")
}
match $format {
"json" => {
_print ($providers | to json) "json" "result" "table"
}
"yaml" => {
_print ($providers | to yaml) "yaml" "result" "table"
}
_ => {
# Table format - show summary or full with --kcl
if $show_kcl {
_print ($providers | to json) "json" "result" "table"
} else {
# Show simplified table
let simplified = ($providers | each {|p|
{name: $p.name, type: $p.type, version: $p.version}
})
_print ($simplified | to json) "json" "result" "table"
}
}
}
}
# Show detailed provider information
def handle_providers_info [args: list, flags: record] {
use ../../lib_provisioning/kcl_module_loader.nu *
if ($args | is-empty) {
print "❌ Provider name required"
print "Usage: provisioning providers info <provider> [--kcl] [--no-cache]"
exit 1
}
let provider_name = $args | get 0
let show_kcl = ($args | any { |x| $x == "--kcl" })
let no_cache = ($args | any { |x| $x == "--no-cache" })
print $"(_ansi blue_bold)📋 Provider Information: ($provider_name)(_ansi reset)"
print ""
let providers = if $no_cache {
(discover-kcl-modules "providers")
} else {
(discover-kcl-modules-cached "providers")
}
let provider_info = ($providers | where name == $provider_name)
if ($provider_info | is-empty) {
print $"❌ Provider not found: ($provider_name)"
exit 1
}
let info = ($provider_info | first)
print $" Name: ($info.name)"
print $" Type: ($info.type)"
print $" Path: ($info.path)"
print $" Has KCL: ($info.has_kcl)"
if $show_kcl and $info.has_kcl {
print ""
print " (_ansi cyan_bold)KCL Module:(_ansi reset)"
print $" Module Name: ($info.kcl_module_name)"
print $" KCL Path: ($info.kcl_path)"
print $" Version: ($info.version)"
print $" Edition: ($info.edition)"
# Check for kcl.mod file
let kcl_mod = ($info.kcl_path | path join "kcl.mod")
if ($kcl_mod | path exists) {
print ""
print $" (_ansi cyan_bold)kcl.mod content:(_ansi reset)"
open $kcl_mod | lines | each {|line| print $" ($line)"}
}
}
print ""
}
# Install provider for infrastructure
def handle_providers_install [args: list, flags: record] {
use ../../lib_provisioning/kcl_module_loader.nu *
if ($args | length) < 2 {
print "❌ Provider name and infrastructure required"
print "Usage: provisioning providers install <provider> <infra> [--version <v>]"
exit 1
}
let provider_name = $args | get 0
let infra_name = $args | get 1
# Extract version flag if present
let version_idx = ($args | enumerate | where item == "--version" | get 0?.index | default (-1))
let version = if $version_idx >= 0 and ($args | length) > ($version_idx + 1) {
$args | get ($version_idx + 1)
} else {
"0.0.1"
}
# Resolve infrastructure path
let infra_path = (resolve_infra_path $infra_name)
if ($infra_path | is-empty) {
print $"❌ Infrastructure not found: ($infra_name)"
exit 1
}
# Install provider
install-provider $provider_name $infra_path --version $version
print ""
print $"(_ansi yellow_bold)💡 Next steps:(_ansi reset)"
print $" 1. Check the manifest: ($infra_path)/providers.manifest.yaml"
print $" 2. Update server definitions to use ($provider_name)"
print $" 3. Run: kcl run defs/servers.k"
}
# Remove provider from infrastructure
def handle_providers_remove [args: list, flags: record] {
use ../../lib_provisioning/kcl_module_loader.nu *
if ($args | length) < 2 {
print "❌ Provider name and infrastructure required"
print "Usage: provisioning providers remove <provider> <infra> [--force]"
exit 1
}
let provider_name = $args | get 0
let infra_name = $args | get 1
let force = ($args | any { |x| $x == "--force" })
# Resolve infrastructure path
let infra_path = (resolve_infra_path $infra_name)
if ($infra_path | is-empty) {
print $"❌ Infrastructure not found: ($infra_name)"
exit 1
}
# Confirmation unless forced
if not $force {
print $"(_ansi yellow)⚠️ This will remove provider ($provider_name) from ($infra_name)(_ansi reset)"
print " KCL dependencies will be updated."
let response = (input "Continue? (y/N): ")
if ($response | str downcase) != "y" {
print "❌ Cancelled"
return
}
}
# Remove provider
remove-provider $provider_name $infra_path
}
# List installed providers for infrastructure
def handle_providers_installed [args: list, flags: record] {
if ($args | is-empty) {
print "❌ Infrastructure name required"
print "Usage: provisioning providers installed <infra> [--format <fmt>]"
exit 1
}
let infra_name = $args | get 0
# Parse format flag
let format_idx = ($args | enumerate | where item == "--format" | get 0?.index | default (-1))
let format = if $format_idx >= 0 and ($args | length) > ($format_idx + 1) {
$args | get ($format_idx + 1)
} else {
"table"
}
# Resolve infrastructure path
let infra_path = (resolve_infra_path $infra_name)
if ($infra_path | is-empty) {
print $"❌ Infrastructure not found: ($infra_name)"
exit 1
}
let manifest_path = ($infra_path | path join "providers.manifest.yaml")
if not ($manifest_path | path exists) {
print $"❌ No providers.manifest.yaml found in ($infra_name)"
exit 1
}
let manifest = (open $manifest_path)
let providers = if ($manifest | get providers? | is-not-empty) {
$manifest | get providers
} else if ($manifest | get loaded_providers? | is-not-empty) {
$manifest | get loaded_providers
} else {
[]
}
print $"(_ansi blue_bold)📦 Installed providers for ($infra_name):(_ansi reset)"
print ""
match $format {
"json" => {
_print ($providers | to json) "json" "result" "table"
}
"yaml" => {
_print ($providers | to yaml) "yaml" "result" "table"
}
_ => {
_print ($providers | to json) "json" "result" "table"
}
}
}
# Validate provider installation
def handle_providers_validate [args: list, flags: record] {
use ../../lib_provisioning/kcl_module_loader.nu *
if ($args | is-empty) {
print "❌ Infrastructure name required"
print "Usage: provisioning providers validate <infra> [--no-cache]"
exit 1
}
let infra_name = $args | get 0
let no_cache = ($args | any { |x| $x == "--no-cache" })
print $"(_ansi blue_bold)🔍 Validating providers for ($infra_name)...(_ansi reset)"
print ""
# Resolve infrastructure path
let infra_path = (resolve_infra_path $infra_name)
if ($infra_path | is-empty) {
print $"❌ Infrastructure not found: ($infra_name)"
exit 1
}
mut validation_errors = []
# Check manifest exists
let manifest_path = ($infra_path | path join "providers.manifest.yaml")
if not ($manifest_path | path exists) {
$validation_errors = ($validation_errors | append "providers.manifest.yaml not found")
} else {
# Check each provider in manifest
let manifest = (open $manifest_path)
let providers = ($manifest | get providers? | default [])
# Load providers once using cache
let all_providers = if $no_cache {
(discover-kcl-modules "providers")
} else {
(discover-kcl-modules-cached "providers")
}
for provider in $providers {
print $" Checking ($provider.name)..."
# Check if provider exists in cached list
let available = ($all_providers | where name == $provider.name)
if ($available | is-empty) {
$validation_errors = ($validation_errors | append $"Provider not found: ($provider.name)")
print $" ❌ Not found in extensions"
} else {
let provider_info = ($available | first)
# Check if symlink exists
let modules_dir = ($infra_path | path join ".kcl-modules")
let link_path = ($modules_dir | path join $provider_info.kcl_module_name)
if not ($link_path | path exists) {
$validation_errors = ($validation_errors | append $"Symlink missing: ($link_path)")
print $" ❌ Symlink not found"
} else {
print $" ✓ OK"
}
}
}
}
# Check kcl.mod
let kcl_mod_path = ($infra_path | path join "kcl.mod")
if not ($kcl_mod_path | path exists) {
$validation_errors = ($validation_errors | append "kcl.mod not found")
}
print ""
# Report results
if ($validation_errors | is-empty) {
print "(_ansi green)✅ Validation passed - all providers correctly installed(_ansi reset)"
} else {
print "(_ansi red)❌ Validation failed:(_ansi reset)"
for error in $validation_errors {
print $" • ($error)"
}
exit 1
}
}
# Helper: Resolve infrastructure path
def resolve_infra_path [infra: string]: nothing -> string {
if ($infra | path exists) {
return $infra
}
# Try workspace/infra path
let workspace_path = $"workspace/infra/($infra)"
if ($workspace_path | path exists) {
return $workspace_path
}
# Try absolute workspace path
let proj_root = ($env.PROVISIONING_ROOT? | default "/Users/Akasha/project-provisioning")
let abs_workspace_path = ($proj_root | path join "workspace" "infra" $infra)
if ($abs_workspace_path | path exists) {
return $abs_workspace_path
}
return ""
}
# Show providers help
def show_providers_help [] {
print $"
(_ansi cyan_bold)╔══════════════════════════════════════════════════╗(_ansi reset)
(_ansi cyan_bold)║(_ansi reset) 📦 PROVIDER MANAGEMENT (_ansi cyan_bold)║(_ansi reset)
(_ansi cyan_bold)╚══════════════════════════════════════════════════╝(_ansi reset)
(_ansi green_bold)[Available Providers](_ansi reset)
(_ansi blue)provisioning providers list [--kcl] [--format <fmt>](_ansi reset)
List all available providers
Formats: table (default value), json, yaml
(_ansi blue)provisioning providers info <provider> [--kcl](_ansi reset)
Show detailed provider information with optional KCL details
(_ansi green_bold)[Provider Installation](_ansi reset)
(_ansi blue)provisioning providers install <provider> <infra> [--version <v>](_ansi reset)
Install provider for an infrastructure
Default version: 0.0.1
(_ansi blue)provisioning providers remove <provider> <infra> [--force](_ansi reset)
Remove provider from infrastructure
--force skips confirmation prompt
(_ansi blue)provisioning providers installed <infra> [--format <fmt>](_ansi reset)
List installed providers for infrastructure
Formats: table (default value), json, yaml
(_ansi blue)provisioning providers validate <infra>(_ansi reset)
Validate provider installation and configuration
(_ansi green_bold)EXAMPLES(_ansi reset)
# List all providers
provisioning providers list
# Show KCL module details
provisioning providers info upcloud --kcl
# Install provider
provisioning providers install upcloud myinfra
# List installed providers
provisioning providers installed myinfra
# Validate installation
provisioning providers validate myinfra
# Remove provider
provisioning providers remove aws myinfra --force
(_ansi default_dimmed)💡 Use 'provisioning help providers' for more information(_ansi reset)
"
2025-10-07 10:32:04 +01:00
}
# Nu shell command handler
def handle_nu [ops: string, flags: record] {
let run_ops = if ($ops | str trim | str starts-with "-") {
""
} else {
let parts = ($ops | split row " ")
if ($parts | is-empty) { "" } else { $parts | first }
2025-10-07 10:32:04 +01:00
}
if ($flags.infra | is-not-empty) and ($env.PROVISIONING_INFRA_PATH | path join $flags.infra | path exists) {
cd ($env.PROVISIONING_INFRA_PATH | path join $flags.infra)
}
if ($flags.output_format | is-empty) {
if ($run_ops | is-empty) {
print (
$"\nTo exit (_ansi purple_bold)NuShell(_ansi reset) session, with (_ansi default_dimmed)lib_provisioning(_ansi reset) loaded, " +
$"use (_ansi green_bold)exit(_ansi reset) or (_ansi green_bold)[CTRL-D](_ansi reset)"
)
# Pass the provisioning configuration files to the Nu subprocess
# This ensures the interactive session has the same config loaded as the calling environment
let config_path = ($env.PROVISIONING_CONFIG? | default "")
# Build library paths argument - needed for module resolution during parsing
# Convert colon-separated string to -I flag arguments
let lib_dirs = ($env.NU_LIB_DIRS? | default "")
let lib_paths = if ($lib_dirs | is-not-empty) {
($lib_dirs | split row ":" | where { |x| ($x | is-not-empty) })
} else {
[]
}
if ($config_path | is-not-empty) {
# Pass config files AND library paths via -I flags for module resolution
# Library paths are set via -I flags which enables module resolution during parsing phase
if ($lib_paths | length) > 0 {
# Construct command with -I flags for each library path
let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts)
# Start interactive Nushell with provisioning configuration loaded
# The -i flag enables interactive mode (REPL) with full terminal features
^nu --config $"($config_path)/config.nu" --env-config $"($config_path)/env.nu" ...$cmd -i
} else {
^nu --config $"($config_path)/config.nu" --env-config $"($config_path)/env.nu" -i
}
} else {
# Fallback if PROVISIONING_CONFIG not set
if ($lib_paths | length) > 0 {
let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts)
^nu ...$cmd -i
} else {
^nu -i
}
}
2025-10-07 10:32:04 +01:00
} else {
# Also pass library paths for single command execution
let lib_dirs = ($env.NU_LIB_DIRS? | default "")
let lib_paths = if ($lib_dirs | is-not-empty) {
($lib_dirs | split row ":" | where { |x| ($x | is-not-empty) })
} else {
[]
}
if ($lib_paths | length) > 0 {
let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts)
^nu ...$cmd -c $"($run_ops)"
} else {
^nu -c $"($run_ops)"
}
2025-10-07 10:32:04 +01:00
}
}
}
# List command handler
def handle_list [ops: string, flags: record] {
let target_list = if ($ops | is-not-empty) {
let parts = ($ops | split row " ")
if ($parts | is-empty) { "" } else { $parts | first }
2025-10-07 10:32:04 +01:00
} else { "" }
let list_ops = ($ops | str replace $"($target_list) " "" | str trim)
on_list $target_list ($flags.onsel | default "") $list_ops
}
# QR code command handler
def handle_qr [] {
make_qr
}
# Nu info command handler
def handle_nuinfo [] {
print $"\n (_ansi yellow)Nu shell info(_ansi reset)"
print (version)
}
# Plugins command handler
def handle_plugins [ops: string, flags: record] {
let subcommand = if ($ops | is-not-empty) {
($ops | split row " " | get 0)
} else {
"list"
}
let remaining_ops = if ($ops | is-not-empty) {
($ops | split row " " | skip 1 | str join " ")
} else {
""
}
match $subcommand {
"list" | "ls" => { handle_plugin_list $flags }
"register" | "add" => { handle_plugin_register $remaining_ops $flags }
"test" => { handle_plugin_test $remaining_ops $flags }
"build" => { handle_plugin_build $remaining_ops $flags }
"status" => { handle_plugin_status $flags }
"help" => { show_plugin_help }
_ => {
print $"❌ Unknown plugin subcommand: ($subcommand)"
print "Use 'provisioning plugin help' for available commands"
exit 1
}
}
}
# List installed plugins with status
def handle_plugin_list [flags: record] {
use ../../lib_provisioning/plugins/mod.nu [list-plugins]
print $"\n (_ansi cyan_bold)Installed Plugins(_ansi reset)\n"
let plugins = (list-plugins)
if ($plugins | length) > 0 {
print ($plugins | table -e)
} else {
print "(_ansi yellow)No plugins found(_ansi reset)"
}
print $"\n(_ansi default_dimmed)💡 Use 'provisioning plugin register <name>' to register a plugin(_ansi reset)"
}
# Register plugin with Nushell
def handle_plugin_register [ops: string, flags: record] {
use ../../lib_provisioning/plugins/mod.nu [register-plugin]
let plugin_name = if ($ops | is-not-empty) {
($ops | split row " " | get 0)
} else {
print $"(_ansi red)❌ Plugin name required(_ansi reset)"
print $"Usage: provisioning plugin register <plugin_name>"
exit 1
}
register-plugin $plugin_name
}
# Test plugin functionality
def handle_plugin_test [ops: string, flags: record] {
use ../../lib_provisioning/plugins/mod.nu [test-plugin]
let plugin_name = if ($ops | is-not-empty) {
($ops | split row " " | get 0)
} else {
print $"(_ansi red)❌ Plugin name required(_ansi reset)"
print $"Usage: provisioning plugin test <plugin_name>"
print $"Valid plugins: auth, kms, tera, kcl"
exit 1
}
test-plugin $plugin_name
}
# Build plugins from source
def handle_plugin_build [ops: string, flags: record] {
use ../../lib_provisioning/plugins/mod.nu [build-plugins]
let plugin_name = if ($ops | is-not-empty) {
($ops | split row " " | get 0)
} else {
""
}
if ($plugin_name | is-empty) {
print $"\n(_ansi cyan)Building all plugins...(_ansi reset)"
build-plugins
} else {
print $"\n(_ansi cyan)Building plugin: ($plugin_name)(_ansi reset)"
build-plugins --plugin $plugin_name
}
}
# Show plugin status
def handle_plugin_status [flags: record] {
use ../../lib_provisioning/plugins/mod.nu [plugin-build-info]
use ../../lib_provisioning/plugins/auth.nu [plugin-auth-status]
use ../../lib_provisioning/plugins/kms.nu [plugin-kms-info]
print $"\n(_ansi cyan_bold)Plugin Status(_ansi reset)\n"
print $"(_ansi yellow_bold)Authentication Plugin:(_ansi reset)"
let auth_status = (plugin-auth-status)
print $" Available: ($auth_status.plugin_available)"
print $" Enabled: ($auth_status.plugin_enabled)"
print $" Mode: ($auth_status.mode)"
print $"\n(_ansi yellow_bold)KMS Plugin:(_ansi reset)"
let kms_info = (plugin-kms-info)
print $" Available: ($kms_info.plugin_available)"
print $" Enabled: ($kms_info.plugin_enabled)"
print $" Backend: ($kms_info.default_backend)"
print $" Mode: ($kms_info.mode)"
print $"\n(_ansi yellow_bold)Build Information:(_ansi reset)"
let build_info = (plugin-build-info)
if $build_info.exists {
print $" Source directory: ($build_info.plugins_dir)"
print $" Available sources: ($build_info.available_sources | length)"
} else {
print $" Source directory: Not found"
}
}
# Show plugin help
def show_plugin_help [] {
print $"
(_ansi cyan_bold)╔══════════════════════════════════════════════════╗(_ansi reset)
(_ansi cyan_bold)║(_ansi reset) 🔌 PLUGIN MANAGEMENT (_ansi cyan_bold)║(_ansi reset)
(_ansi cyan_bold)╚══════════════════════════════════════════════════╝(_ansi reset)
(_ansi green_bold)[Plugin Operations](_ansi reset)
(_ansi blue)plugin list(_ansi reset) List all plugins with status
(_ansi blue)plugin register <name>(_ansi reset) Register plugin with Nushell
(_ansi blue)plugin test <name>(_ansi reset) Test plugin functionality
(_ansi blue)plugin build [name](_ansi reset) Build plugins from source
(_ansi blue)plugin status(_ansi reset) Show plugin status and info
(_ansi green_bold)[Available Plugins](_ansi reset)
• (_ansi cyan)auth(_ansi reset) - JWT authentication with MFA support
• (_ansi cyan)kms(_ansi reset) - Key Management Service integration
• (_ansi cyan)tera(_ansi reset) - Template rendering engine
• (_ansi cyan)kcl(_ansi reset) - KCL configuration language
(_ansi green_bold)EXAMPLES(_ansi reset)
# List all plugins
provisioning plugin list
# Register auth plugin
provisioning plugin register nu_plugin_auth
# Test KMS plugin
provisioning plugin test kms
# Build all plugins
provisioning plugin build
# Build specific plugin
provisioning plugin build nu_plugin_auth
# Show plugin status
provisioning plugin status
(_ansi default_dimmed)💡 Plugins provide HTTP fallback when not registered
Authentication and KMS work in both plugin and HTTP modes(_ansi reset)
"
}
# Guide command handler
def handle_guide [ops: string, flags: record] {
let guide_topic = if ($ops | is-not-empty) {
($ops | split row " " | get 0)
} else {
""
}
# Define guide topics and their paths
let guides = {
"quickstart": "docs/guides/quickstart-cheatsheet.md",
"from-scratch": "docs/guides/from-scratch.md",
"scratch": "docs/guides/from-scratch.md",
"start": "docs/guides/from-scratch.md",
"deploy": "docs/guides/from-scratch.md",
"list": "list_guides"
}
# Get docs directory
let docs_dir = ($env.PROVISIONING_PATH | path join "docs" "guides")
match $guide_topic {
"" => {
# Show guide list
show_guide_list $docs_dir
}
"list" => {
show_guide_list $docs_dir
}
_ => {
# Try to find and display guide
let guide_path = if ($guide_topic in ($guides | columns)) { $guides | get $guide_topic } else { null }
if ($guide_path == null or $guide_path == "list_guides") {
print $"(_ansi red)❌ Unknown guide:(_ansi reset) ($guide_topic)"
print ""
show_guide_list $docs_dir
exit 1
}
let full_path = ($env.PROVISIONING_PATH | path join $guide_path)
if not ($full_path | path exists) {
print $"(_ansi red)❌ Guide file not found:(_ansi reset) ($full_path)"
exit 1
}
# Display guide using best available viewer
display_guide $full_path $guide_topic
}
}
}
# Display guide using best available markdown viewer
def display_guide [
guide_path: path
topic: string
] {
print $"\n(_ansi cyan_bold)📖 Guide:(_ansi reset) ($topic)\n"
# Check for viewers in order of preference: glow, bat, less, cat
if (which glow | length) > 0 {
^glow $guide_path
} else if (which bat | length) > 0 {
^bat --style=plain --paging=always $guide_path
} else if (which less | length) > 0 {
^less $guide_path
} else {
open $guide_path
}
}
# Show list of available guides
def show_guide_list [docs_dir: path] {
print $"
(_ansi magenta_bold)╔══════════════════════════════════════════════════╗(_ansi reset)
(_ansi magenta_bold)║(_ansi reset) 📚 AVAILABLE GUIDES (_ansi magenta_bold)║(_ansi reset)
(_ansi magenta_bold)╚══════════════════════════════════════════════════╝(_ansi reset)
(_ansi green_bold)[Step-by-Step Guides](_ansi reset)
(_ansi blue)provisioning guide from-scratch(_ansi reset)
Complete deployment from zero to production
(_ansi default_dimmed)Shortcuts: scratch, start, deploy(_ansi reset)
(_ansi green_bold)[Quick References](_ansi reset)
(_ansi blue)provisioning guide quickstart(_ansi reset)
Command shortcuts and quick reference
(_ansi default_dimmed)Shortcuts: shortcuts, quick(_ansi reset)
(_ansi green_bold)USAGE(_ansi reset)
# View guide
provisioning guide <topic>
# List all guides
provisioning guide list
provisioning howto (_ansi default_dimmed)# shortcut(_ansi reset)
(_ansi green_bold)EXAMPLES(_ansi reset)
# Complete deployment guide
provisioning guide from-scratch
# Quick command reference
provisioning guide quickstart
(_ansi green_bold)VIEWING TIPS(_ansi reset)
• (_ansi cyan)Best experience:(_ansi reset) Install glow for beautiful rendering
(_ansi default_dimmed)brew install glow # macOS(_ansi reset)
• (_ansi cyan)Alternative:(_ansi reset) bat provides syntax highlighting
(_ansi default_dimmed)brew install bat # macOS(_ansi reset)
• (_ansi cyan)Fallback:(_ansi reset) less/cat work on all systems
(_ansi default_dimmed)💡 All guides provide copy-paste ready commands
Perfect for quick start and reference!(_ansi reset)
"
2025-10-07 10:32:04 +01:00
}