#!/usr/bin/env nu # Info: Script to run Provisioning # Author: JesusPerezLorenzo # Release: 1.0.4 # Date: 6-2-2024 #use std # assert use std log # Detect project root and set up module paths early # This ensures NU_LIB_DIRS is properly configured before loading modules export-env { # Project root detection: look for kcl.mod or provisioning structure let potential_roots = [ $env.PWD ($env.PWD | path dirname) ($env.PWD | path dirname | path dirname) ] let matching_roots = ($potential_roots | where ($it | path join "kcl.mod" | path exists) or ($it | path join "core" "nulib" | path exists)) let project_root = if ($matching_roots | length) > 0 { $matching_roots | first } else { $env.PWD } # Update PWD in NU_LIB_DIRS to use detected project root if ($env.NU_LIB_DIRS? | default [] | any {|path| $path == $env.PWD}) { $env.NU_LIB_DIRS = ($env.NU_LIB_DIRS | each {|path| if $path == $env.PWD { $project_root } else { $path } }) } # Add project-local env if it exists - will be loaded after main env.nu } use lib_provisioning * use env.nu * #Load all main defs use main_provisioning * # Load new modular command system use main_provisioning/flags * use main_provisioning/dispatcher * #module srv { use instances.nu * } use servers/ssh.nu * use servers/utils.nu * use taskservs/utils.nu find_taskserv # Help on provisioning commands export def "main help" [ category?: string # Optional category: infrastructure, orchestration, development, workspace, concepts --notitles # not titles --out: string # Print Output format: json, yaml, text (default) ] { if $notitles == null or not $notitles { show_titles } if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false } print (provisioning_options $category) if not $env.PROVISIONING_DEBUG { end_run "" } } def main [ ...args: string # Other options, use help to get info --infra (-i): string # Cloud directory --settings (-s): string # Settings path --serverpos (-p): int # Server position in settings --outfile (-o): string # Output file --template(-t): string # Template path or name in PROVISION_KLOUDS_PATH --check (-c) # Only check mode no servers will be created --yes (-y) # confirm task --wait (-w) # Wait servers to be created --keepstorage # keep storage --select: string # Select with task as option --onsel: string # On selection: e (edit) | v (view) | l (list) | t (tree) --infras: string # Infra list names separated by commas --new (-n): string # New infrastructure name --debug (-x) # Use Debug mode --xm # Debug with PROVISIONING_METADATA --xc # Debug for task and services locally PROVISIONING_DEBUG_CHECK --xr # Debug for remote servers PROVISIONING_DEBUG_REMOTE --xld # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug --nc # Not clean working settings --metadata # Error with metadata (-xm) --notitles # not tittles --environment: string # Environment override (dev/test/prod) -v # Show version --version (-V) # Show version with title --info (-I) # Show Info with title --about (-a) # Show About --helpinfo (-h) # For more details use options "help" (no dashes) --out: string # Print Output format: json, yaml, text (default) --view # Print with highlight --inputfile: string # Input format: json, yaml, text (default) --include_notuse # Include servers not use ]: nothing -> nothing { # Initialize provisioning system provisioning_init $helpinfo "" $args # Parse all flags into normalized structure let parsed_flags = (parse_common_flags { version: $version, v: $v, info: $info, about: $about, debug: $debug, metadata: $metadata, xc: $xc, xr: $xr, xld: $xld, check: $check, yes: $yes, wait: $wait, keepstorage: $keepstorage, nc: $nc, include_notuse: $include_notuse, out: $out, notitles: $notitles, view: $view, infra: $infra, infras: $infras, settings: $settings, outfile: $outfile, template: $template, select: $select, onsel: $onsel, serverpos: $serverpos, new: $new, environment: $environment }) # Handle version, info, about flags if $parsed_flags.show_version { ^$env.PROVISIONING_NAME -v ; exit } if $parsed_flags.show_info { ^$env.PROVISIONING_NAME -i ; exit } if $parsed_flags.show_about { _print (get_about_info) ; exit } # Dispatch command to appropriate handler dispatch_command $args $parsed_flags # End run if not in debug mode if not ($env.PROVISIONING_DEBUG? | default false) { end_run "" } # "upcloud" => { # #use upcloud/servers.nu upcloud # if $infra == null { # upcloud $args # } else { # upcloud --infra $infra $args # } # }, # "aws" => { # #use aws/servers.nu aws # if $infra == null { # aws $args # } else { # aws --infra $infra $args # } # }, # "local" => { # #use local/servers.nu local # if $infra == null { # local $args # } else { # local --infra $infra $args # } # }, "h" => { ^($env.PROVISIONING_NAME) help ($env.PROVISIONING_ARGS | str replace $task '') "--notitles" }, "cache" => { let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_outfile = if $outfile != null { $"--outfile ($outfile) "} else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_infra) ($str_out) ($str_outfile)" "server" "cache" }, "providers" => { #use defs/lists.nu * _print $"(_ansi green)PROVIDERS(_ansi reset) list: \n" _print (providers_list "selection" | to json) "json" "result" "table" }, "ssh" => { #use servers/ssh.nu * #use utils/settings.nu * let curr_settings = (find_get_settings --infra $infra --settings $settings $include_notuse) rm -rf $curr_settings.wk_path server_ssh $curr_settings "" "pub" false } "sed" | "sops" => { if ($ops | length) == -2 { (throw-error $"๐Ÿ›‘ No file found" $"for (_ansi yellow_bold)sops(_ansi reset) edit") exit -1 } let pos = if $task == "sed" { 0 } else { 1 } let target_file = ($ops | get -o $pos | default "") let target_full_path = if ($target_file | path exists) == false { let infra_path = (get_infra $infra) if ($infra_path | path join $target_file | path exists) { ($infra_path | path join $target_file) } 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 } if $env.PROVISIONING_SOPS? == null { let curr_settings = (find_get_settings --infra $infra --settings $settings $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 } #use sops on_sops if $task == "sed" { on_sops "sed" $target_full_path } else { on_sops $task $target_full_path ($ops | skip 1) } }, "e" | "env" => { # Check if this is the new environment management system let subcmd = ($ops | get -o 0 | default "") if $subcmd in ["list" "current" "switch" "validate" "compare" "show" "init" "detect" "set" "paths" "create" "delete" "export" "status"] { # Use new environment management system use lib_provisioning/cmd/environment.nu * match $subcmd { "list" => { env list } "current" => { env current } "switch" => { let target_env = ($ops | get -o 1 | default "") if ($target_env | is-empty) { print "Usage: env switch " exit 1 } env switch $target_env } "validate" => { let target_env = ($ops | get -o 1 | default "") env validate $target_env } "compare" => { let env1 = ($ops | get -o 1 | default "") let env2 = ($ops | get -o 2 | default "") if ($env1 | is-empty) or ($env2 | is-empty) { print "Usage: env compare " exit 1 } env compare $env1 $env2 } "show" => { let target_env = ($ops | get -o 1 | default "") env show $target_env } "init" => { let target_env = ($ops | get -o 1 | default "") if ($target_env | is-empty) { print "Usage: env init " exit 1 } env init $target_env } "detect" => { env detect } "set" => { let target_env = ($ops | get -o 1 | default "") if ($target_env | is-empty) { print "Usage: env set " exit 1 } env set $target_env } "paths" => { let target_env = ($ops | get -o 1 | default "") env paths $target_env } "create" => { let target_env = ($ops | get -o 1 | default "") if ($target_env | is-empty) { print "Usage: env create " exit 1 } env create $target_env } "delete" => { let target_env = ($ops | get -o 1 | default "") if ($target_env | is-empty) { print "Usage: env delete " exit 1 } env delete $target_env } "export" => { let target_env = ($ops | get -o 1 | default "") env export $target_env } "status" => { let target_env = ($ops | get -o 1 | default "") env status $target_env } _ => { print "Environment Management Commands:" print " env list - List available environments" print " env current - Show current environment" print " env switch - Switch to environment" print " env validate [env] - Validate environment" print " env compare - Compare environments" print " env show [env] - Show environment config" print " env init - Initialize environment" print " env detect - Detect current environment" print " env set - Set environment variable" print " env paths [env] - Show environment paths" print " env create - Create new environment" print " env delete - Delete environment" print " env export [env] - Export environment config" print " env status [env] - Show environment status" } } } else { # Fall back to legacy environment display match $out { "json" => { _print (show_env | to json) "json" "result" "table" }, "yaml" => { _print (show_env | to yaml) "yaml" "result" "table" }, "toml" => { _print (show_env | to toml) "toml" "result" "table" }, _ => { print (show_env | table -e) } , } } }, "allenv" => { let all_env = { env: (show_env), providers: (on_list "providers" "-" ""), taskservs: (on_list "taskservs" "-" ""), clusters: (on_list "clusters" "-" ""), infras: (on_list "infras" "-" ""), itemdefs: { providers: (find_provgendefs), taskserv: ( open ($env.PROVISIONING_TASKSERVS_PATH | path join $env.PROVISIONING_GENERATE_DIRPATH | path join $env.PROVISIONING_GENERATE_DEFSFILE) ) } } if ($view) { match $out { "json" => ($all_env | to json | highlight), "yaml" => ($all_env | to yaml | highlight), "toml" => ($all_env | to toml | highlight), _ => ($all_env | to json | highlight), } } else { match $out { "json" => { _print ($all_env | to json) "json" "result" "table" }, "yaml" => { _print ($all_env | to yaml) "yaml" "result" "table" }, "toml" => { _print ($all_env | to toml) "toml" "result" "table" }, _ => { print ($all_env | to json) } , } } }, "show" => { match ($ops | get -o 0 | default "") { "h" |"help" => { print (provisioning_show_options) exit }, } let curr_settings = (find_get_settings --infra $infra --settings $settings $include_notuse) if ($curr_settings | is-empty) { if ($out | is-empty) { _print $"๐Ÿ›‘ Errors found in infra (_ansi yellow_bold)($infra)(_ansi reset) notuse ($include_notuse)" print ($curr_settings | describe) print $settings } exit } let show_info = (get_show_info $ops $curr_settings ($out | default "")) if ($view) { match $out { "json" => { print ($show_info | to json | highlight json) }, "yaml" => { print ($show_info | to yaml | highlight yaml) }, "toml" => { print ($show_info | to toml | highlight toml) }, _ => { print ($show_info | to json | highlight) }, } } else { match $out { "json" => { _print ($show_info | to json) "json" "result" "table" }, "yaml" => { _print ($show_info | to yaml) "yaml" "result" "table" }, "toml" => { _print ($show_info | to toml) "toml" "result" "table" }, _ => { print ($show_info | to json) } , } } }, "c" | "create" => { let use_debug = if $debug or $env.PROVISIONING_DEBUG { "-x"} else { "" } let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" } exec $"($env.PROVISIONING_NAME)" $use_debug "create" $str_ops $use_check $str_infra $str_out --notitles }, "d" | "delete" => { let use_debug = if $debug { "-x"} else { "" } let use_check = if $check { "--check "} else { "" } let use_yes = if $yes { "--yes "} else { "" } let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } exec $"($env.PROVISIONING_NAME)" "delete" $str_ops $use_check $use_yes $use_keepstorage $str_infra --notitles }, "u" | "update" => { let use_debug = if $debug { "-x"} else { "" } let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } exec $"($env.PROVISIONING_NAME)" "update" $str_ops $use_check $str_infra --notitles }, "cst" | "create-server-task" | "csts" | "create-servers-tasks" => { run_module $str_ops "server" "create" if $env.LAST_EXIT_CODE != 0 { _print $"๐Ÿ›‘ Errors found in (_ansi yellow_bold)create-server(_ansi reset)" exit 1 } run_module $"- ($str_ops)" "taskserv" "create" }, "s" | "server" => { let use_check = if $check { "--check "} else { "" } let use_yes = if $yes { "--yes" } else { "" } let use_wait = if $wait { "--wait" } else { "" } let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_outfile = if $outfile != null { $"--outfile ($outfile) "} else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } let arg_include_notuse = if $include_notuse { $"--include_notuse "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check) ($str_out) ($str_outfile) ($use_yes) ($use_wait) ($use_keepstorage) ($arg_include_notuse)" "server" --exec }, "price" | "prices" | "cost" | "costs" => { let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check) ($str_out)" "server" "price" --exec }, "t" | "task" | "taskserv" => { let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check)" "taskserv" --exec }, "cl" | "cluster" => { let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check)" "cluster" --exec }, "g" | "gen" | "generate" => { match ($ops | get -o 0 | default "") { "h" |"help" => { print (provisioning_generate_options) exit }, } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let use_debug = if $debug { "-x"} else { "" } let use_check = if $check { "--check "} else { "" } let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" } let str_input = if $inputfile != null { $"--inputfile ($inputfile) "} else { "" } let str_template = if ($template != null) { $"--template ($template)" } else { "" } let str_select = if ($select != null) { $"--select ($select)" } else { "" } if ($str_ops | is-empty) { exec $"($env.PROVISIONING_NAME)" $use_debug "generate" $str_ops $use_check $str_infra $str_out --notitles } else { let target = ($ops | get -o 0) let gen_ops = ($ops | skip 1 | str join " ") + $" ($str_infra) ($str_template) ($str_out) ($use_check) ($use_debug) ($str_select) ($str_input)" match $target { "s" | "server" => { run_module $"- ($gen_ops)" "server" "generate" --exec }, "t" | "task" | "taskserv" => { run_module $"- ($gen_ops)" "taskserv" "generate" --exec }, "i" | "infra" | "infras" => { run_module $"- ($gen_ops)" "infra" "generate" --exec }, "cl" | "cluster" => { run_module $"- ($gen_ops)" "cluster" "generate" --exec }, "h" | "help" => { _print $"\n(provisioning_generate_options)" exit }, "new" => { exec $"($env.PROVISIONING_NAME)" $use_debug "generate" "new" $gen_ops $str_template $use_check $str_infra $str_out --notitles }, "_" => { invalid_task "" $target --end exit } } } print $"($str_ops) ($str_infra)" #generate }, "ctx" | "context" => { ^$"($env.PROVISIONING_NAME)" "context" $str_ops --notitles run_module $str_ops "" --exec }, "setup" | "st" | "config" => { run_module $str_ops "setup" --exec }, "init" => { # Initialize user configuration match ($ops | get -o 0 | default "") { "config" => { # Initialize user config with template selection use lib_provisioning/config/loader.nu init-user-config let template_type = ($ops | get -o 1 | default "user") let force_flag = ($ops | any {|op| $op == "--force" or $op == "-f"}) print "๐Ÿš€ Initializing user configuration" print "==================================" print "" init-user-config --template $template_type --force $force_flag } "help" | "h" => { print "๐Ÿ“‹ Init Command Help" print "====================" print "" print "Initialize user configuration from templates:" print "" print "Commands:" print " init config [template] [--force] Initialize user config" print "" print "Templates:" print " user General user configuration (default)" print " dev Development environment optimized" print " prod Production environment optimized" print " test Testing environment optimized" print "" print "Options:" print " --force, -f Overwrite existing configuration" print "" print "Examples:" print " provisioning init config" print " provisioning init config dev" print " provisioning init config prod --force" } _ => { print "โŒ Unknown init command. Use 'provisioning init help' for available options." } } }, "tpl" | "tmpl" | "template" => { # Workspace template management (infrastructure patterns) let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check) ($str_out)" "template" --exec } "ws" | "workspace" => { # Workspace management let use_check = if $check { "--check "} else { "" } let use_yes = if $yes { "--yes" } else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check) ($use_yes) ($str_out)" "workspace" --exec } "mod" | "module" => { let use_check = if $check { "--check "} else { "" } let str_infra = if $infra != null { $"--infra ($infra) "} else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_infra) ($use_check) ($str_out)" "module" --exec } "lyr" | "layer" => { let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_out)" "layer" --exec } "version" => { let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_out)" "version" --exec } "pack" => { let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_out)" "pack" --exec } "wf" | "flow" | "workflow" => { let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($str_out)" "workflow" --exec } "bat" | "batch" => { let use_check = if $check { "--check "} else { "" } let use_wait = if $wait { "--wait" } else { "" } let str_out = if $out != null { $"--out ($out) "} else { "" } run_module $"($str_ops) ($use_check) ($use_wait) ($str_out)" "batch" --exec } "orch" | "orchestrator" => { run_module $"($str_ops)" "orchestrator" --exec } "control-center" => { run_module $"($str_ops)" "control-center" --exec } "mcp-server" => { run_module $"($str_ops)" "mcp-server" --exec } "config-template" => { # Configuration template management (config.*.toml templates) match ($ops | get -o 0 | default "") { "list" => { print "๐Ÿ“‹ Available Configuration Templates" print "===================================" print "" let project_root = $env.PWD let templates = [ { name: "user", file: "config.user.toml.example", description: "General user configuration with comprehensive documentation" } { name: "dev", file: "config.dev.toml.example", description: "Development environment with enhanced debugging" } { name: "prod", file: "config.prod.toml.example", description: "Production environment with security and performance focus" } { name: "test", file: "config.test.toml.example", description: "Testing environment with mock providers and CI/CD integration" } ] for template in $templates { let template_path = ($project_root | path join $template.file) let status = if ($template_path | path exists) { "โœ…" } else { "โŒ" } print $"($status) ($template.name) - ($template.description)" if ($template_path | path exists) { print $" ๐Ÿ“ ($template_path)" } else { print $" โŒ Template file not found: ($template_path)" } print "" } print "๐Ÿ’ก Usage: provisioning init config [template_name]" } "show" => { # Show template content let template_name = ($ops | get -o 1 | default "") if ($template_name | is-empty) { print "โŒ Please specify a template name. Use 'provisioning config-template list' to see available templates." return } let template_file = match $template_name { "user" => "config.user.toml.example" "dev" => "config.dev.toml.example" "prod" => "config.prod.toml.example" "test" => "config.test.toml.example" _ => { print $"โŒ Unknown template: ($template_name). Valid options: user, dev, prod, test" return } } let project_root = $env.PWD let template_path = ($project_root | path join $template_file) if not ($template_path | path exists) { print $"โŒ Template file not found: ($template_path)" return } print $"๐Ÿ“„ Template: ($template_name)" print $"๐Ÿ“ Path: ($template_path)" print "=" * 80 print "" # Show template content using configured file viewer let file_viewer = ($env.PROVISIONING_FILE_VIEWER? | default "less") ^$file_viewer $template_path } "validate" => { # Validate all templates print "๐Ÿ” Validating Configuration Templates" print "=====================================" print "" let project_root = $env.PWD let templates = ["config.user.toml.example", "config.dev.toml.example", "config.prod.toml.example", "config.test.toml.example"] mut all_valid = true for template in $templates { let template_path = ($project_root | path join $template) print $"๐Ÿ” Validating ($template)..." if not ($template_path | path exists) { print $" โŒ File not found: ($template_path)" $all_valid = false continue } # Basic TOML syntax validation and section checking print " โœ… TOML syntax assumed valid (template file)" let config_data = (open $template_path) # Check if config_data is a record (properly parsed TOML) if (($config_data | describe) == "record") { let required_sections = ["core", "paths", "debug", "output"] for section in $required_sections { if ($config_data | get -o $section | is-not-empty) { print $" โœ… Required section present: ($section)" } else { print $" โš ๏ธ Section missing or empty: ($section)" } } } else { print " โš ๏ธ Template contains only comments/documentation (no active config sections)" } print "" } if $all_valid { print "โœ… All templates validated successfully!" } else { print "โŒ Some templates have validation issues" } } "help" | "h" => { print "๐Ÿ“‹ Configuration Template Command Help" print "======================================" print "" print "Manage configuration file templates (config.*.toml):" print "" print "Commands:" print " config-template list List available config templates" print " config-template show Show template content" print " config-template validate Validate all templates" print "" print "Examples:" print " provisioning config-template list" print " provisioning config-template show dev" print " provisioning config-template validate" } _ => { print "โŒ Unknown config-template command. Use 'provisioning config-template help' for available options." } } }, "i" | "infra" | "infras" => { if ($str_ops | str contains "help") or $str_ops == "h" { run_module "help" "infra" exit -2 } let infra_ops = if ($infra | is-not-empty) { $"-i ($infra)" } else if $infras != null { $"--infras ($infras)" } else { $"-i (get_infra | path basename)" } let use_yes = if $yes { "--yes"} else { "" } let use_check = if $check { "--check"} else { "" } let use_onsel = if $onsel != null { $"--onsel ($onsel)"} else { "" } run_module $"($str_ops) ($infra_ops) ($use_check) ($use_onsel) ($use_yes)" "infra" }, "deploy-rm" | "deploy-del" | "dp-rm" | "d-r" | "destroy" => { let curr_settings = (find_get_settings --infra $infra --settings $settings) deploy_remove $curr_settings ($str_ops | split row "-" | get -o 0 | default "") rm -rf $curr_settings.wk_path if $task == "destroy" { let with_yes = if $yes { "--yes" } else { "" } exec $"($env.PROVISIONING_NAME)" "delete" server --notitles $with_yes } }, "deploy-sel" | "deploy-list" | "dp-sel" | "d-s" => { let curr_settings = (find_get_settings --infra $infra --settings $settings) deploy_list $curr_settings ($str_ops | split row "-" | get -o 0 | default "") ($onsel | default "") rm -rf $curr_settings.wk_path }, "deploy-sel-tree" | "deploy-list-tree" | "dp-sel-t" | "d-st" => { let curr_settings = (find_get_settings --infra $infra --settings $settings) (deploy_list $curr_settings $str_ops "tree") rm -rf $curr_settings.wk_path }, "nu" => { let run_ops = if ($str_ops | str trim | str starts-with "-") { "" } else { ($ops | get -o 0) } if ($infra | is-not-empty) and ($env.PROVISIONING_INFRA_PATH | path join $infra |path exists) { cd ($env.PROVISIONING_INFRA_PATH | path join $infra) } if ($env.PROVISIONING_OUT | 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)" ) ^nu -i -e $"use lib_provisioning * ; use env.nu * ; show_titles;" #^nu -e $"use lib_provisioning * ; show_titles; $env.PROMPT_INDICATOR = {|| 'provisioning> ' } ; $env.PROMPT_COMMAND = {|| create_left_prompt } " } else { ^nu -c $"($run_ops)" } } }, "list" | "l" | "ls" => { #use defs/lists.nu on_list let target_list = if ($args | length) > -1 { ($args| get -o 1 | default "") } else { "" } let list_ops = ($ops | str join " " | str replace $"($target_list) " "" | str trim) on_list $target_list ($onsel | default "") $list_ops }, "qr" => { #use utils/qr.nu * make_qr }, "nuinfo" => { print $"\n (_ansi yellow)Nu shell info(_ansi reset)" print (version) }, "plugin" | "plugins" => { print $"\n (_ansi yellow)Nu shell Plugins(_ansi reset)" ^nu -c "plugin list" }, "new" => { let str_new = ($new | default "") print $"\n (_ansi yellow)New Infra ($str_new)(_ansi reset)" }, "ai" => { # AI command module let str_infra = if $infra != null { $"--infra ($infra) " } else { "" } let str_settings = if $settings != null { $"--settings ($settings) " } else { "" } let str_out = if $out != null { $"--out ($out) " } else { "" } run_module $"($str_ops) ($str_infra) ($str_settings) ($str_out)" "ai" --exec }, "validate" | "val" => { # Infrastructure validation module let sub_command = ($ops | get -o 0 | default "") match $sub_command { "help" | "h" => { use main_provisioning/ops.nu * print (provisioning_validate_options) } "config" => { # Configuration validation print "๐Ÿ” Configuration Validation" print "==========================" print "" # Load configuration using the config loader use lib_provisioning/config/loader.nu * let config_result = (load-provisioning-config --debug $debug --validate false) # Run detailed validation let strict_mode = (($ops | get -o 1 | default "") == "strict") let validation_result = (validate-config $config_result --detailed true --strict $strict_mode) print $"๐Ÿ“Š Validation Summary:" print $" โ€ข Structure valid: ($validation_result.summary.structure_valid)" print $" โ€ข Paths valid: ($validation_result.summary.paths_valid)" print $" โ€ข Types valid: ($validation_result.summary.types_valid)" print $" โ€ข Semantic rules valid: ($validation_result.summary.semantic_valid)" print $" โ€ข File references valid: ($validation_result.summary.files_valid)" print "" if ($validation_result.errors | length) > 0 { print "โŒ Errors found:" for error in $validation_result.errors { print $" โ€ข [($error.severity | str upcase)] ($error.message)" } print "" } if ($validation_result.warnings | length) > 0 { print "โš ๏ธ Warnings found:" for warning in $validation_result.warnings { print $" โ€ข [($warning.severity | str upcase)] ($warning.message)" } print "" } if $validation_result.valid { print "โœ… Configuration validation passed!" print "" print " All configuration checks completed successfully." print " Configuration is ready for use." exit 0 } else { print "โŒ Configuration validation failed!" print "" print " Please fix the errors above before proceeding." exit 1 } } "interpolation" | "interp" => { # Enhanced interpolation validation and testing use lib_provisioning/config/loader.nu * let interp_command = ($ops | get -o 1 | default "test") match $interp_command { "test" => { # Test interpolation with sample data let sample_type = ($ops | get -o 2 | default "basic") test-interpolation --sample $sample_type } "validate" => { # Validate interpolation patterns in current config print "๐Ÿ” Interpolation Pattern Validation" print "===================================" print "" let config_result = (load-provisioning-config --debug $debug --validate false) let validation_result = (validate-interpolation $config_result --detailed true) if $validation_result.valid { print "โœ… Interpolation validation passed!" print $" โ€ข ($validation_result.summary.interpolation_patterns_detected) patterns processed" } else { print "โŒ Interpolation validation failed!" for error in $validation_result.errors { print $" โ€ข [ERROR] ($error.message)" } } if ($validation_result.warnings | length) > 0 { print "" print "โš ๏ธ Warnings:" for warning in $validation_result.warnings { print $" โ€ข [WARNING] ($warning.message)" } } print "" print $"๐Ÿ“Š Summary: ($validation_result.summary.total_errors) errors, ($validation_result.summary.total_warnings) warnings" } "testsuite" | "suite" => { # Run comprehensive interpolation test suite print "๐Ÿงช Running Comprehensive Interpolation Test Suite" print "=================================================" print "" let output_file = ($ops | get -o 2 | default "interpolation_test_results.json") let suite_results = (create-interpolation-test-suite --output-file $output_file) print "" print "๐ŸŽฏ Test Suite Results:" print $" Success rate: ($suite_results.success_rate)%" print $" Total tests: ($suite_results.total)" print $" Passed: ($suite_results.passed)" print $" Failed: ($suite_results.failed)" if $suite_results.failed > 0 { exit 1 } } "show" => { # Show interpolation patterns and results let mode = ($ops | get -o 2 | default "interpolated") print "๐Ÿ“‹ Configuration Interpolation View" print "====================================" print "" let config_result = (load-provisioning-config --debug $debug --validate false) match $mode { "raw" => { print "๐Ÿ” Raw configuration (before interpolation):" print "" # Load config without interpolation let raw_config = (load-provisioning-config --debug $debug --validate false) print ($raw_config | to json) } "interpolated" => { print "โœจ Interpolated configuration:" print "" print ($config_result | to json) } "patterns" => { print "๐Ÿ” Detected interpolation patterns:" print "" # Convert to JSON and find all patterns let json_str = ($config_result | to json) # Simple pattern detection if ($json_str | str contains "{{") { print " Found interpolation patterns in configuration" # This is a simplified implementation let pattern_count = (count-interpolation-patterns $json_str) print $" Total patterns: ($pattern_count)" } else { print " No interpolation patterns found" } } _ => { print "โŒ Unknown show mode. Valid options: raw, interpolated, patterns" } } } "help" | "h" => { print "๐Ÿ“‹ Interpolation Command Help" print "==============================" print "" print "Enhanced interpolation features for advanced configuration templating:" print "" print "Commands:" print " validate interpolation test [sample] Test interpolation with sample data" print " validate interpolation validate Validate interpolation patterns" print " validate interpolation show [mode] Show interpolation results" print " validate interpolation testsuite [file] Run comprehensive test suite" print "" print "Sample Types:" print " basic Basic interpolation patterns ({{paths.base}}, {{env.HOME}})" print " advanced Advanced patterns (git info, providers, conditionals)" print " all All interpolation features" print "" print "Show Modes:" print " raw Show configuration before interpolation" print " interpolated Show configuration after interpolation (default)" print " patterns Show detected interpolation patterns" print "" print "Supported Interpolation Patterns:" print " {{paths.base}} Base path interpolation" print " {{env.HOME}}, {{env.USER}} Environment variables" print " {{now.date}}, {{now.timestamp}} Date/time values" print " {{git.branch}}, {{git.commit}} Git repository information" print " {{sops.key_file}} SOPS configuration" print " {{providers.aws.region}} Cross-section references" print " {{path.join(paths.base, \"dir\")}} Function calls" print " {{env.HOME || \"/tmp\"}} Conditional expressions" print "" print "Examples:" print " provisioning validate interpolation test basic" print " provisioning validate interpolation validate" print " provisioning validate interpolation show raw" print " provisioning validate interpolation testsuite" } _ => { print "โŒ Unknown interpolation command. Use 'provisioning validate interpolation help' for options." } } } "test" => { # Run the test script directly nu test_validation.nu } "quick" => { let target_path = if $infra != null { $infra } else { let next_arg = ($ops | get -o 1 | default ".") if ($next_arg | path exists) { $next_arg } else { "." } } print "๐Ÿš€ Quick Infrastructure Validation" print "==================================" print "" print $"๐Ÿ“ Target: ($target_path | path expand)" print "" print "๐Ÿ”„ Running quick validation (errors and critical issues only)..." print "" let result = (nu test_validation.nu | complete) if $result.exit_code == 0 { print "โœ… Quick validation passed!" print "" print " No critical errors or blocking issues found." print $" Infrastructure ($target_path) is ready for deployment." } else { print "โŒ Quick validation found issues" print "" print " Please review and fix critical/error-level issues before deployment." } print "" } "rules" => { # Show rules list print "๐Ÿ“‹ Available Validation Rules" print "============================" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL001: ๐Ÿšจ YAML Syntax Validation (critical)" print " Category: syntax | Severity: critical | Auto-fix: false" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL002: ๐Ÿšจ KCL Compilation Check (critical)" print " Category: compilation | Severity: critical | Auto-fix: false" print "" print "๐Ÿ”ง โœ… VAL003: โŒ Unquoted Variable References (error)" print " Category: syntax | Severity: error | Auto-fix: true" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL004: โŒ Required Fields Validation (error)" print " Category: schema | Severity: error | Auto-fix: false" print "" print "๐Ÿ”ง โœ… VAL005: โš ๏ธ Resource Naming Conventions (warning)" print " Category: best_practices | Severity: warning | Auto-fix: true" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL006: โŒ Basic Security Checks (error)" print " Category: security | Severity: error | Auto-fix: false" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL007: โš ๏ธ Version Compatibility Check (warning)" print " Category: compatibility | Severity: warning | Auto-fix: false" print "" print "๐Ÿ”ง ๐Ÿ‘๏ธ VAL008: โŒ Network Configuration Validation (error)" print " Category: networking | Severity: error | Auto-fix: false" print "" print "Legend:" print "โœ… = Auto-fixable | ๐Ÿ‘๏ธ = Manual fix required" print "๐Ÿšจ = Critical | โŒ = Error | โš ๏ธ = Warning | โ„น๏ธ = Info" } _ => { # Execute actual validation let target_path = if $infra != null { $infra } else if ($sub_command | path exists) { $sub_command } else { # Use current directory if it contains infrastructure files # Check for common infrastructure indicators: settings.k, kcl.mod, or .k files let current_dir = "." let has_settings = ($current_dir | path join "settings.k" | path exists) let has_kcl_mod = ($current_dir | path join "kcl.mod" | path exists) let has_k_files = ((glob "*.k") | length) > 0 if $has_settings or $has_kcl_mod or $has_k_files { $current_dir } else { # If no infrastructure files in current dir, show help use main_provisioning/ops.nu * print (provisioning_validate_options) return } } print "๐Ÿ” Infrastructure Validation & Review Tool" print "==========================================" print "" print $"๐Ÿ“ Validating: ($target_path | path expand)" print "" # Check if target path exists if not ($target_path | path exists) { print $"๐Ÿ›‘ Infrastructure path not found: ($target_path)" print "" print "Use 'provisioning validate help' for usage information" exit 1 } # Run the validation using our working test system print "๐Ÿ”„ Running infrastructure validation..." print "" # Run basic validation directly without external script dependency # Count and validate infrastructure files recursively let k_files = (glob "**/*.k") let yaml_files = (glob "**/*.yaml" | append (glob "**/*.yml")) let toml_files = (glob "**/*.toml") let total_files = ($k_files | length) + ($yaml_files | length) + ($toml_files | length) print $"๐Ÿ“Š Found ($total_files) infrastructure files:" print $" โ€ข KCL files: ($k_files | length)" print $" โ€ข YAML files: ($yaml_files | length)" print $" โ€ข TOML files: ($toml_files | length)" print "" # Simple validation checks mut issues = [] # Check for settings.k file if ("settings.k" | path exists) { print "โœ… settings.k file found" } else { print "โš ๏ธ No settings.k file found" $issues = ($issues | append "Missing settings.k file") } # Basic KCL syntax check for each .k file for file in $k_files { print $"๐Ÿ” Checking KCL file: ($file)" # Check if file is SOPS encrypted let content = (open $file --raw) let is_sops_file = ($content | str contains "\"sops\":") or ($content | str contains "ENC[AES256_GCM") if $is_sops_file { # Handle SOPS encrypted file print $" ๐Ÿ” ($file) - SOPS encrypted file detected" # Set up SOPS environment using the config-driven approach $env.PROVISIONING_USE_SOPS = ($env.PROVISIONING_USE_SOPS? | default "age") $env.PROVISIONING_SOPS = ($env.PROVISIONING_SOPS? | default "") use lib_provisioning/sops/lib.nu get_def_age let kage_path = (get_def_age $env.PROVISIONING_KLOUD_PATH) if ($kage_path | is-not-empty) and ($kage_path | path exists) { $env.SOPS_AGE_KEY_FILE = $kage_path } # Check if SOPS can decrypt it let sops_check = (^sops -d $file | complete) if $sops_check.exit_code == 0 { # Try to validate the decrypted content let kcl_check = (^sops -d $file | ^kcl - | complete) if $kcl_check.exit_code == 0 { print $" โœ… ($file) - SOPS encrypted KCL syntax OK" } else { print $" โŒ ($file) - SOPS encrypted KCL syntax error" $issues = ($issues | append $"KCL syntax error in SOPS file ($file)") } } else { print $" โš ๏ธ ($file) - SOPS decryption failed - check keys/config" print $" Skipping validation (SOPS error: ($sops_check.stderr | str trim))" # Don't add to issues - this might be expected if keys aren't available } } else { # Regular KCL file validation let check_result = (^kcl $file | complete) if $check_result.exit_code == 0 { print $" โœ… ($file) - KCL syntax OK" } else { print $" โŒ ($file) - KCL syntax error" $issues = ($issues | append $"KCL syntax error in ($file)") } } } # Basic YAML syntax check for file in $yaml_files { print $"๐Ÿ” Checking YAML file: ($file)" let yaml_result = (^yq eval . $file | complete) if $yaml_result.exit_code == 0 { print $" โœ… ($file) - YAML syntax OK" } else { print $" โŒ ($file) - YAML syntax error" $issues = ($issues | append $"YAML syntax error in ($file)") } } let result = { exit_code: (if ($issues | length) > 0 { 1 } else { 0 }) issues: $issues } print "" if $result.exit_code == 0 { print "โœ… Validation completed successfully!" print "" print "๐Ÿ“Š Summary:" print " โ€ข No critical issues found" print " โ€ข All infrastructure files are valid" print " โ€ข Infrastructure is ready for deployment" print "" print $"๐Ÿ“ Files processed in: ($target_path | path expand)" print "" print "๐Ÿ’ก For detailed validation options, use:" print " provisioning validate help" } else { print "โŒ Validation found issues" print "" print "๐Ÿ” Issues found:" for issue in $result.issues { print $" โ€ข ($issue)" } print "" print "๐Ÿ’ก Please fix these issues before deployment" print " Use 'provisioning validate help' for more options" } } } }, _ => { invalid_task "" $task --end exit }, } if not $env.PROVISIONING_DEBUG { end_run "" } #print $"($env.PWD)\n($env.FILE_PWD)\n($env.PROCESS_PATH)\n" } export def get_show_info [ ops: list curr_settings: record out: string ]: nothing -> record { match ($ops | get -o 0 | default "") { "set" |"setting" | "settings" => $curr_settings, "def" | "defs" |"defsetting" | "defsettings" => { let src = ($curr_settings | get -o src | default ""); let src_path = ($curr_settings | get -o src_path | default ""); let def_settings = if ($src_path | path join $src | path exists) { open -r ($src_path | path join $src) } else { "" } let main_path = ($env.PROVISIONING | path join "kcl" | path join "settings.k") let src_main_settings = if ($main_path | path exists) { open -r $main_path } else { "" } { def: $src, def_path: $src_path, infra: ($curr_settings | get -o infra | default ""), infra_path: ($curr_settings | get -o infra_path | default ""), def_settings: $def_settings, main_path: $main_path, main_settings: $src_main_settings, } }, "server" |"servers" | "s" => { let servers = ($curr_settings | get -o data | get -o servers | default {}) let item = ($ops | get -o 1 | default "") if ($item | is-empty) { $servers } else { let server = (find_server $item $servers ($out | default "")) let def_target = ($ops | get -o 2 | default "") match $def_target { "t" | "task" | "taskserv" => { let task = ($ops | get -o 3 | default "") (find_taskserv $curr_settings $server $task ($out | default "")) }, _ => $server, } } }, "serverdefs" |"serversdefs" | "sd" => { (find_serversdefs $curr_settings) }, "provgendefs" |"provgendef" | "pgd" => { (find_provgendefs) }, "taskservs" |"taskservs" | "ts" => { #(list_taskservs $curr_settings) let list_taskservs = (taskservs_list) if ($list_taskservs | length) == 0 { _print $"๐Ÿ›‘ no items found for (_ansi cyan)taskservs list(_ansi reset)" return } $list_taskservs }, "taskservsgendefs" |"taskservsgendef" | "tsd" => { let defs_path = ($env.PROVISIONING_TASKSERVS_PATH | path join $env.PROVISIONING_GENERATE_DIRPATH | path join $env.PROVISIONING_GENERATE_DEFSFILE) if ($defs_path | path exists) { open $defs_path } }, "cost" | "costs" | "c" | "price" | "prices" | "p" => { (servers_walk_by_costs $curr_settings "" false false "stdout") }, "alldata" => ($curr_settings | get -o data | default {} | merge { costs: (servers_walk_by_costs $curr_settings "" false false "stdout") } ), "data" | _ => { if ($out | is-not-empty) { ($curr_settings | get -o data | default {}) } else { print ($" (_ansi cyan_bold)($curr_settings | get -o data | get -o main_name | default '')" + $"(_ansi reset): (_ansi yellow_bold)($curr_settings | get -o data | get -o main_title | default '') (_ansi reset)" ) print ($curr_settings | get -o data | default {} | merge { servers: ''}) ($curr_settings | get -o data | default {} | get -o servers | each {|item| print $"\n server: (_ansi cyan_bold)($item.hostname | default '') (_ansi reset)" print $item }) "" } }, } }