#!/usr/bin/env nu # Info: Script to run Provisioning # Author: JesusPerezLorenzo # Release: 1.0.4 # Date: 6-2-2024 # CRITICAL: Must be in export-env block so it runs DURING PARSING, # not after. This sets up NU_LIB_DIRS before modules are loaded. export-env { # Initialize NU_LIB_DIRS, handling both string (from bash) and list (from Nushell) let lib_dirs_raw = ($env.NU_LIB_DIRS? | default "") let current_lib_dirs = if ($lib_dirs_raw | type) == "string" { if ($lib_dirs_raw | is-empty) { [] } else { ($lib_dirs_raw | split row ":") } } else { $lib_dirs_raw } # Ensure known provisioning paths are in NU_LIB_DIRS let default_paths = [ "/opt/provisioning/core/nulib" "/usr/local/provisioning/core/nulib" ] # Combine paths: use default paths first, then add any from current $env.NU_LIB_DIRS = ($default_paths | append $current_lib_dirs) } use std log use lib_provisioning * use env.nu * #Load all main defs use main_provisioning * #module srv { use instances.nu * } use servers/ssh.nu * use servers/utils.nu * use taskservs/utils.nu find_taskserv use lib_provisioning/platform/bootstrap.nu * # Helper: Reorder arguments to put flags before positional args # This allows: provisioning workspace update --yes # Instead of requiring: provisioning --yes workspace update def reorder_args [args: list]: nothing -> list { let flags = ($args | where {|x| ($x | str starts-with "-")}) let positionals = ($args | where {|x| not ($x | str starts-with "-")}) ($flags | append $positionals) } # Help on provisioning commands export def "main help" [ ...args: 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 } # Use only the first argument, ignore any extras (e.g., "orch status" -> "orch") let category = if ($args | length) > 0 { ($args | get 0) } else { "" } 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) --dep-option: string # Workspace dependency option: workspace-home, home-package, git-package, publish-repo --dep-url: string # Dependency URL for git-package or publish-repo --dry-run # Show what would be done without doing it (pack command) --force (-f) # Skip confirmation prompts (pack/delete commands) --all # Process all items (pack clean command) --keep-latest: int # Keep N latest versions (pack clean command) --activate # Activate workspace as default (workspace commands) --interactive # Interactive workspace creation wizard --org: string # Organization name (for detect/complete commands) --apply # Apply changes (for complete command) --verbose # Verbose output (for detect/complete/workflow commands) --pretty # Pretty-print JSON/YAML output (for detect/complete commands) -v # Show version --version (-V) # Show version with title --info # Show Info with title --about # 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 { # Reorder arguments: move flags to the beginning # This allows: provisioning workspace update --yes let reordered_args = (reorder_args $args) # Extract flags from reordered args (for flags that came after positional args) let has_yes_in_args = ($reordered_args | any {|x| $x == "--yes" or $x == "-y"}) let has_check_in_args = ($reordered_args | any {|x| $x == "--check" or $x == "-c"}) let has_force_in_args = ($reordered_args | any {|x| $x == "--force" or $x == "-f"}) let has_verbose_in_args = ($reordered_args | any {|x| $x == "--verbose" or $x == "-v"}) let has_wait_in_args = ($reordered_args | any {|x| $x == "--wait" or $x == "-w"}) # Combine with already-parsed flags (take OR - if either parsed or in args, then true) let final_yes = ($yes or $has_yes_in_args) let final_check = ($check or $has_check_in_args) let final_force = ($force or $has_force_in_args) let final_verbose = ($verbose or $has_verbose_in_args) let final_wait = ($wait or $has_wait_in_args) # Initialize provisioning system provisioning_init $helpinfo "" $reordered_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: $final_check, yes: $final_yes, wait: $final_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, dep_option: $dep_option, dep_url: $dep_url, dry_run: $dry_run, force: $final_force, all: $all, keep_latest: $keep_latest, activate: $activate, interactive: $interactive, org: $org, apply: $apply, verbose: $final_verbose, pretty: $pretty }) # 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 } # Bootstrap platform services (only if running actual commands, not help/info) # Skip bootstrap for help-like, guide, setup, discovery/info, and utility commands let is_help_command = ( ($reordered_args | length) == 0 or ($reordered_args | get 0) in [ # Help and guides "help", "-h", "--help", "sc", "shortcuts", "quickstart", "quick", "from-scratch", "scratch", "customize", "custom", "guide", "guides", "howto", # Setup "setup", "st", # Discovery and module commands "mod", "module", "discover", "disc", "dt", "dp", "dc", "discover-taskservs", "disc-t", "discover-providers", "disc-p", "discover-clusters", "disc-c", # Development info "lyr", "layer", "version", "pack", # Utilities and info "nuinfo", "env", "allenv", "validate", "val", "show", "config-template", "cache", "list", "l", "ls", "plugin", "plugins", "qr", "ssh", "sops", "providers", "status", "health" ] ) if not $is_help_command { let bootstrap_result = (bootstrap-platform --auto-start --timeout=60 --verbose=($final_verbose)) if not $bootstrap_result.all_healthy { _print "" _print $"(_ansi red)❌ Platform services not healthy(_ansi reset)" _print "" _print "Failed services:" for service in ($bootstrap_result.services | where {|s| $s.status != "healthy"}) { _print $" - ($service.name): ($service.action)" } _print "" _print "To start services manually:" _print " cd provisioning/platform && docker-compose up -d" _print "" exit 1 } } # For info/discovery/utility commands, dispatch directly without going through workspace enforcement # These commands don't need workspace context if (($reordered_args | length) > 0) and (($reordered_args | get 0) in [ # Guide commands "guide", "guides", "sc", "howto", "shortcuts", "quickstart", "quick", "from-scratch", "scratch", "customize", "custom", # Discovery/info commands "mod", "module", "discover", "disc", "dt", "dp", "dc", "discover-taskservs", "disc-t", "discover-providers", "disc-p", "discover-clusters", "disc-c", "lyr", "layer", "version", "nuinfo", "env", "allenv", "validate", "val", "show", "cache", # Utility commands (these are informational) "plugin", "plugins", "qr", "nuinfo", "status", "health" ]) { dispatch_command $reordered_args $parsed_flags if not $env.PROVISIONING_DEBUG { end_run "" } return } # Dispatch command to appropriate handler dispatch_command $reordered_args $parsed_flags # End run if not in debug mode if not ($env.PROVISIONING_DEBUG? | default false) { end_run "" } } 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 }) "" } }, } }