#!/usr/bin/env nu # Info: Script to run Provisioning Template Management # Author: JesusPerezLorenzo # Release: 1.0.0 # Date: 29-09-2025 use std log use lib_provisioning * use env.nu * # - > Help on Template export def "main help" [ --src: string = "" --notitles # not titles --out: string # Print Output format: json, yaml, text (default) ]: nothing -> nothing { if $notitles == null or not $notitles { show_titles } ^$"($env.PROVISIONING_NAME)" -mod template --help if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false } print (provisioning_options $src) if not $env.PROVISIONING_DEBUG { end_run "" } } # > Template Management def main [ ...args: string # Other options, use help to get info -v # Show version -i # Show Info --version (-V) # Show version with title --info (-I) # Show Info with title --about (-a) # Show About --infra: string # Infrastructure name --provider: string = "" # Provider name --type: string = "" # Template type: taskservs, providers, servers, clusters --layer: string = "workspace" # Layer: core, workspace, infra --dry-run # Show what would be done without doing it --backup # Create backup before operations --force (-f) # Force operation --check (-c) # Only check mode, no actual changes --yes (-y) # Confirm task --debug (-x) # Use Debug mode --xm # Debug with PROVISIONING_METADATA --xld # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug --metadata # Error with metadata (-xm) --notitles # Do not show banner titles --helpinfo (-h) # For more details use options "help" (no dashes) --out: string # Print Output format: json, yaml, text (default) ]: nothing -> nothing { if ($out | is-not-empty) { $env.PROVISIONING_OUT = $out $env.PROVISIONING_NO_TERMINAL = true } provisioning_init $helpinfo "template" $args if $version or $v { ^$env.PROVISIONING_NAME -v ; exit } if $info or $i { ^$env.PROVISIONING_NAME -i ; exit } if $about { _print (get_about_info) exit } if $debug { $env.PROVISIONING_DEBUG = true } if $metadata { $env.PROVISIONING_METADATA = true } let task = if ($args | length) > 0 { ($args | get 0) } else { "" } let ops = $"($env.PROVISIONING_ARGS? | default "") " | str replace $" ($task) " "" | str trim let template_name = if ($ops | is-not-empty) and not ($ops | str starts-with "-") { ($ops | split row " " | get 0) } else { "" } let target_infra = if ($ops | split row " " | length) > 1 { ($ops | split row " " | get 1) } else { $infra } $env.PROVISIONING_MODULE = "template" match $task { "h" | "help" => { # Redirect to main categorized help system exec $"($env.PROVISIONING_NAME)" help workspace --notitles }, "list" | "ls" => { template_list --type $type --out $out }, "types" => { template_types --out $out }, "show" => { if ($template_name | is-empty) { print "❌ Template name required" print "Usage: provisioning template show " exit 1 } template_show $template_name --type $type --out $out }, "apply" => { if ($template_name | is-empty) or ($target_infra | is-empty) { print "❌ Template name and target infrastructure required" print "Usage: provisioning template apply --provider " exit 1 } if $dry_run { template_apply $template_name $target_infra --provider $provider --dry-run } else { template_apply $template_name $target_infra --provider $provider } }, "extract" => { if ($target_infra | is-empty) { print "❌ Infrastructure name required" print "Usage: provisioning template extract " exit 1 } if $dry_run { template_extract $target_infra --type $type --dry-run } else { template_extract $target_infra --type $type } }, "convert" => { if ($target_infra | is-empty) { print "❌ Infrastructure name required" print "Usage: provisioning template convert " exit 1 } if $backup and $dry_run { template_convert $target_infra --backup --dry-run } else if $backup { template_convert $target_infra --backup } else if $dry_run { template_convert $target_infra --dry-run } else { template_convert $target_infra } }, "validate" => { if ($target_infra | is-empty) { print "❌ Infrastructure name required" print "Usage: provisioning template validate " exit 1 } template_validate $target_infra }, "layer" => { template_layer_info --infra $target_infra --out $out }, _ => { print $"❌ Unknown task: ($task)" print "Use 'provisioning template help' for available commands" exit 1 } } } # List available templates def template_list [ --type: string --out: string ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let templates_path = ($provisioning_root | path join "workspace" "templates") if not ($templates_path | path exists) { print "❌ Templates directory not found" exit 1 } let template_types = if ($type | is-not-empty) { [$type] } else { ["taskservs", "providers", "servers", "clusters"] } let all_templates = ($template_types | each {|ttype| let type_path = ($templates_path | path join $ttype) if ($type_path | path exists) { let items = (ls $type_path | where type == dir | select name) $items | each {|item| { type: $ttype name: ($item.name | path basename) path: $item.name }} } else { [] } } | flatten) if ($out | is-not-empty) { if $out == "json" { print ($all_templates | to json) } else if $out == "yaml" { print ($all_templates | to yaml) } else { print ($all_templates | table) } } else { print "📋 Available Templates:" for template in $all_templates { print $" [($template.type)] ($template.name)" } } } # List template types def template_types [--out: string]: nothing -> nothing { let types = [ {type: "taskservs", description: "Task service configurations (kubernetes, containerd, etc.)"} {type: "providers", description: "Cloud provider templates (upcloud, aws, local)"} {type: "servers", description: "Server configuration patterns"} {type: "clusters", description: "Complete cluster templates"} ] if ($out | is-not-empty) { if $out == "json" { print ($types | to json) } else if $out == "yaml" { print ($types | to yaml) } else { print ($types | table) } } else { print "📁 Template Types:" for t in $types { print $" ($t.type): ($t.description)" } } } # Show template details def template_show [ name: string --type: string --out: string ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let templates_path = ($provisioning_root | path join "workspace" "templates") let template_types = if ($type | is-not-empty) { [$type] } else { ["taskservs", "providers", "servers", "clusters"] } let found = ($template_types | each {|ttype| let template_path = ($templates_path | path join $ttype $name) if ($template_path | path exists) { let files = (ls $template_path | select name type size) { type: $ttype name: $name path: $template_path files: $files } } else { null } } | where {|x| $x != null} | first) if ($found | is-empty) { print $"❌ Template not found: ($name)" exit 1 } if ($out | is-not-empty) { _print $found } else { print $"📦 Template: ($found.name)" print $" Type: ($found.type)" print $" Path: ($found.path)" print " Files:" for file in $found.files { print $" - ($file.name | path basename) [($file.type)] ($file.size)" } } } # Apply template to infrastructure def template_apply [ template_name: string target_infra: string --provider: string --dry-run ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let templates_path = ($provisioning_root | path join "workspace" "templates") # Find template let template_types = ["taskservs", "providers", "servers", "clusters"] let template_path = ($template_types | each {|ttype| let path = ($templates_path | path join $ttype $template_name) if ($path | path exists) { {type: $ttype, path: $path} } else { null } } | where {|x| $x != null} | first) if ($template_path | is-empty) { print $"❌ Template not found: ($template_name)" exit 1 } let config = get-config let workspace_root = $config.paths.base? | default $env.PWD let target_path = ($workspace_root | path join "infra" $target_infra) if not ($target_path | path exists) { print $"❌ Infrastructure not found: ($target_infra)" exit 1 } if $dry_run { print $"🔍 DRY RUN: Would apply template ($template_name) to ($target_infra)" print $" Template type: ($template_path.type)" print $" Template path: ($template_path.path)" print $" Target path: ($target_path)" print $" Provider: ($provider)" } else { print $"🔧 Applying template ($template_name) to ($target_infra)" let target_subdir = match $template_path.type { "taskservs" => "task-servs" "providers" => "defs" "servers" => "defs" "clusters" => "clusters" _ => $template_path.type } let dest_dir = ($target_path | path join $target_subdir) mkdir $dest_dir # Copy template files cp -r ($template_path.path | path join "*") $dest_dir print $"✅ Template applied: ($template_name) -> ($target_infra)" print $"📁 Files copied to: ($dest_dir)" } } # Extract patterns from infrastructure def template_extract [ infra_name: string --type: string --dry-run ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu") if not ($migrate_script | path exists) { print $"❌ Migration script not found: ($migrate_script)" exit 1 } let type_flag = if ($type | is-not-empty) { ["--type" $type] } else { [] } let dry_run_flag = if $dry_run { ["--dry-run"] } else { [] } ^nu $migrate_script extract $infra_name ...$type_flag ...$dry_run_flag } # Convert infrastructure to use templates def template_convert [ infra_name: string --backup --dry-run ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu") if not ($migrate_script | path exists) { print $"❌ Migration script not found: ($migrate_script)" exit 1 } let backup_flag = if not $backup { ["--no-backup"] } else { [] } let dry_run_flag = if $dry_run { ["--dry-run"] } else { [] } ^nu $migrate_script convert $infra_name ...$backup_flag ...$dry_run_flag } # Validate template usage in infrastructure def template_validate [infra_name: string]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu") if not ($migrate_script | path exists) { print $"❌ Migration script not found: ($migrate_script)" exit 1 } ^nu $migrate_script validate $infra_name } # Show layer information def template_layer_info [ --infra: string --out: string ]: nothing -> nothing { let layers = [ { name: "core" priority: 100 path: "provisioning/extensions/" description: "Base provisioning system extensions" } { name: "workspace" priority: 200 path: "provisioning/workspace/templates/" description: "Shared templates and reusable configurations" } { name: "infrastructure" priority: 300 path: $"workspace/infra/($infra | default '{name}')/" description: "Infrastructure-specific configurations and overrides" } ] if ($out | is-not-empty) { if $out == "json" { print ($layers | to json) } else if $out == "yaml" { print ($layers | to yaml) } else { print ($layers | table) } } else { print "🏗️ Layer Resolution Order:" print " (Higher priority overrides lower priority)" print "" for layer in ($layers | reverse) { print $" ($layer.priority) - ($layer.name | str upcase)" print $" Path: ($layer.path)" print $" ($layer.description)" print "" } } }