# NOTE: Nickel plugin is loaded for direct access to nickel-export command #export-env { # if ((version).installed_plugins | str contains "nickel") { # plugin use nickel # } #} #plugin rm "~/.local/bin/nu_plugin_nickel" #plugin add "~/.local/bin/nu_plugin_nickel" use ../config/accessor.nu * use ./logging.nu * use ./nickel_processor.nu * use ./error.nu [throw-error] use ./init.nu [get-provisioning-infra-path get-provisioning-name get-provisioning-resources] # Get default settings filename (Nickel format post-migration) def get-default-settings [] : nothing -> string { "settings.ncl" } use ../../../../extensions/providers/prov_lib/middleware.nu * use ../context.nu * use ../sops/mod.nu * use ../workspace/detection.nu * use ../user/config.nu * # No-op function for backward compatibility # This function was used to set workspace context but is now handled by config system export def set-wk-cnprov [ wk_path: string ] { # Config system now handles workspace context automatically # This function remains for backward compatibility } export def find_get_settings [ --infra (-i): string # Infra directory --settings (-s): string # Settings path include_notuse: bool = false no_error: bool = false ] { #use utils/settings.nu [ load_settings ] if $infra != null { if $settings != null { (load_settings --infra $infra --settings $settings $include_notuse $no_error) } else { (load_settings --infra $infra $include_notuse $no_error) } } else { if $settings != null { (load_settings --settings $settings $include_notuse $no_error) } else { (load_settings $include_notuse $no_error) } } } export def check_env [ ] { # TuDO true } export def get_context_infra_path [ ] { let context = (setup_user_context) if $context == null or $context.infra == null { return "" } if $context.infra_path? != null and ($context.infra_path | path join $context.infra | path exists) { return ($context.infra_path| path join $context.infra) } if ((get-provisioning-infra-path) | path join $context.infra | path exists) { return ((get-provisioning-infra-path) | path join $context.infra) } "" } export def get_infra [ infra?: string --workspace: string = "" ] { # Priority 1: Explicit --infra flag (highest) if ($infra | is-not-empty) { if ($infra | path exists) { $infra } else if ($infra | path join (get-default-settings) | path exists) { $infra } else if ((get-provisioning-infra-path) | path join $infra | path join (get-default-settings) | path exists) { (get-provisioning-infra-path) | path join $infra } else { # Try to find in workspace infra directory # Wrap get-effective-workspace so an unregistered workspace doesn't abort early let effective_ws = if ($workspace | is-not-empty) { $workspace } else { do -i { get-effective-workspace } | default "" } let ws_path = if ($effective_ws | is-not-empty) { do -i { get-workspace-path $effective_ws } | default "" } else { "" } let ws_infra_path = if ($ws_path | is-not-empty) { [$ws_path "infra" $infra] | path join } else { "" } if ($ws_infra_path | is-not-empty) and ($ws_infra_path | path exists) { $ws_infra_path } else { # PWD fallback: when inside a workspace dir that has infra/ let pwd_candidate = ($env.PWD | path join "infra" $infra) if ($pwd_candidate | path exists) { $pwd_candidate } else { let text = $"($infra) on ((get-provisioning-infra-path) | path join $infra)" (throw-error "🛑 Path not found " $text "get_infra" --span (metadata $infra).span) } } } } else { # Priority 2: PWD detection if ($env.PWD | path join (get-default-settings) | path exists) { return $env.PWD } let pwd_infra = (detect-infra-from-pwd) if ($pwd_infra | is-not-empty) { let effective_ws = if ($workspace | is-not-empty) { $workspace } else { (get-effective-workspace) } let ws_path = (get-workspace-path $effective_ws) let infra_path = ([$ws_path "infra" $pwd_infra] | path join) if ($infra_path | path join (get-default-settings) | path exists) { return $infra_path } } # Priority 2.5: workspace root config/provisioning.ncl in PWD (no registration needed) let ws_config_file = ($env.PWD | path join "config" "provisioning.ncl") if ($ws_config_file | path exists) { let current_infra = (ncl-eval-soft $ws_config_file [] null | get -o current_infra | default "") if ($current_infra | is-not-empty) { let infra_path = ($env.PWD | path join "infra" $current_infra) if ($infra_path | path join (get-default-settings) | path exists) { return $infra_path } } } # Priority 2.6: convention — workspace dir name = infra name (zero-config fallback) let convention_path = ($env.PWD | path join "infra" ($env.PWD | path basename)) if ($convention_path | path join (get-default-settings) | path exists) { return $convention_path } # Priority 3: Default infra from workspace config # Try PWD-inferred workspace first so CWD takes precedence over the active workspace let effective_ws = if ($workspace | is-not-empty) { $workspace } else { let inferred = do -i { infer-workspace-from-pwd } | default "" if ($inferred | is-not-empty) { $inferred } else { do -i { get-effective-workspace } | default "" } } let default_infra = (get-workspace-default-infra $effective_ws) if ($default_infra | is-not-empty) { let ws_path = (get-workspace-path $effective_ws) let infra_path = ([$ws_path "infra" $default_infra] | path join) if ($infra_path | path join (get-default-settings) | path exists) { return $infra_path } } # Priority 4: session config — infra.current (consulted only after all PWD checks fail) let session_infra = (do -i { config-get "infra.current" "" } | default "") if ($session_infra | is-not-empty) { let effective_ws2 = if ($workspace | is-not-empty) { $workspace } else { do -i { get-effective-workspace } | default "" } let ws_path2 = if ($effective_ws2 | is-not-empty) { do -i { get-workspace-path $effective_ws2 } | default "" } else { "" } let session_infra_path = if ($ws_path2 | is-not-empty) { [$ws_path2 "infra" $session_infra] | path join } else { "" } if ($session_infra_path | is-not-empty) and ($session_infra_path | path join (get-default-settings) | path exists) { return $session_infra_path } } # Fallback: Context-based resolution if ((get-provisioning-infra-path) | path join ($env.PWD | path basename) | path join (get-default-settings) | path exists) { return ((get-provisioning-infra-path) | path join ($env.PWD | path basename)) } let context_path = get_context_infra_path if $context_path != "" { return $context_path } # Last resort: return workspace root let effective_ws = if ($workspace | is-not-empty) { $workspace } else { (get-effective-workspace) } (get-workspace-path $effective_ws) } } export def parse_nickel_file [ src: string target: string append: bool msg: string err_exit?: bool = false ] { # Guard: Check source file exists if not ($src | path exists) { let text = $"nickel source not found: ($src)" (throw-error $msg $text "parse_nickel_file" --span (metadata $src).span) if $err_exit { exit 1 } return false } # Process Nickel file let format = if (get-work-format) == "json" { "json" } else { "yaml" } let raw_out = (process_nickel_export_raw $src $format) let result = (^nu -c $"'($raw_out)' | from json") if ($result | is-empty) { let text = $"nickel ($src) compilation failed - check Nickel syntax" (throw-error $msg $text "parse_nickel_file" --span (metadata $src).span) if $err_exit { exit 1 } return false } if $append { $result | save --append $target } else { $result | save -f $target } true } export def load_from_wk_format [ src: string ] { if not ( $src | path exists) { return {} } let data_raw = (open -r $src) if (get-work-format) == "json" { $data_raw | from json | default {} } else { $data_raw | from yaml | default {} } } export def load_defaults [ src_path: string item_path: string target_path: string ] { if ($target_path | path exists) { if (is_sops_file $target_path) { decode_sops_file $src_path $target_path true } retrurn } let full_path = if ($item_path | path exists) { ($item_path) } else if ($"($item_path).ncl" | path exists) { $"($item_path).ncl" } else if ($src_path | path dirname | path join $"($item_path).ncl" | path exists) { $src_path | path dirname | path join $"($item_path).ncl" } else { "" } if $full_path == "" { return true } if (is_sops_file $full_path) { decode_sops_file $full_path $target_path true (parse_nickel_file $target_path $target_path false $"🛑 load default settings failed ($target_path) ") } else { (parse_nickel_file $full_path $target_path false $"🛑 load default settings failed ($full_path)") } } export def get_provider_env [ settings: record server: record ] { let prov_env_path = if ($server.prov_settings | path exists ) { $server.prov_settings } else { let file_path = ($settings.src_path | path join $server.prov_settings) if ($file_path | str ends-with '.ncl' ) { $file_path } else { $"($file_path).ncl" } } if not ($prov_env_path| path exists ) { if (is-debug-enabled) { _print $"🛑 load (_ansi cyan_bold)provider_env(_ansi reset) from ($server.prov_settings) failed at ($prov_env_path)" } return {} } let str_created_taskservs_dirpath = ($settings.data.created_taskservs_dirpath | default "/tmp" | str replace "\~" $env.HOME | str replace "NOW" $env.NOW | str replace "./" $"($settings.src_path)/") let created_taskservs_dirpath = if ($str_created_taskservs_dirpath | str starts-with "/" ) { $str_created_taskservs_dirpath } else { $settings.src_path | path join $str_created_taskservs_dirpath } if not ( $created_taskservs_dirpath | path exists) { ^mkdir -p $created_taskservs_dirpath } let source_settings_path = ($created_taskservs_dirpath | path join $"($prov_env_path | path basename)") let target_settings_path = ($created_taskservs_dirpath| path join $"($prov_env_path | path basename | str replace '.ncl' '').((get-work-format))") let res = if (is_sops_file $prov_env_path) { decode_sops_file $prov_env_path $source_settings_path true (parse_nickel_file $source_settings_path $target_settings_path false $"🛑 load prov settings failed ($target_settings_path)") } else { cp $prov_env_path $source_settings_path (parse_nickel_file $source_settings_path $target_settings_path false $"🛑 load prov settings failed ($prov_env_path)") } if not (is-debug-enabled) { rm -f $source_settings_path } if $res and ($target_settings_path | path exists) { let data = (open $target_settings_path) if not (is-debug-enabled) { rm -f $target_settings_path } $data } else { {} } } export def get_file_format [ filename: string ] { if ($filename | str ends-with ".json") { "json" } else if ($filename | str ends-with ".yaml") { "yaml" } else { (get-work-format) } } export def save_provider_env [ data: record settings: record provider_path: string ] { if ($provider_path | is-empty) or not ($provider_path | path dirname |path exists) { _print $"❗ Can not save provider env for (_ansi blue)($provider_path | path dirname)(_ansi reset) in (_ansi red)($provider_path)(_ansi reset )" return } if (get_file_format $provider_path) == "json" { $"data: ($data | to json | encode base64)" | save --force $provider_path } else { $"data: ($data | to yaml | encode base64)" | save --force $provider_path } let result = (on_sops "encrypt" $provider_path --quiet) if ($result | is-not-empty) { ($result | save --force $provider_path) } } export def get_provider_data_path [ settings: record server: record ] { # Get prov_data_dirpath with fallbacks for different settings structures let prov_data_dir = ( $settings.data.prov_data_dirpath? | default ($settings.prov_data_dirpath? | default "./data") ) let data_path = if ($prov_data_dir | str starts-with "." ) { let base = ($settings.src_path? | default ($settings.infra_path? | default ".")) ($base | path join $prov_data_dir) } else { $prov_data_dir } if not ($data_path | path exists) { ^mkdir -p $data_path } ($data_path | path join $"($server.provider)_cache.((get-work-format))") } export def load_provider_env [ settings: record server: record provider_path: string = "" ] { let data = if ($provider_path | is-not-empty) and ($provider_path |path exists) { let file_data = if (is_sops_file $provider_path) { on_sops "decrypt" $provider_path --quiet let result = (on_sops "decrypt" $provider_path --quiet) # --character-set binhex if (get_file_format $provider_path) == "json" { ($result | from json | get data? | default "" | decode base64 | decode | from json) } else { ($result | from yaml | get data? | default "" | decode base64 | decode | from yaml) } } else { open $provider_path } if ($file_data | is-empty) or ($file_data | get main?.vpc? | default null) == "?" { # (throw-error $"load provider ($server.provider) settings failed" $"($provider_path) no main data" # "load_provider_env" --span (metadata $data).span) if (is-debug-enabled) { _print $"load provider ($server.provider) settings failed ($provider_path) no main data in load_provider_env" } {} } else { $file_data } } else { {} } if ($data | is-empty) { let new_data = (get_provider_env $settings $server) if ($new_data | is-not-empty) and ($provider_path | is-not-empty) { save_provider_env $new_data $settings $provider_path } $new_data } else { $data } } export def load_provider_settings [ settings: record server: record ] { let data_path = if ($settings.data.settings.prov_data_dirpath | str starts-with "." ) { ($settings.src_path | path join $settings.data.settings.prov_data_dirpath) } else { $settings.data.settings.prov_data_dirpath } if ($data_path | is-empty) { (throw-error $"load provider ($server.provider) settings failed" $"($settings.data.settings.prov_data_dirpath)" "load_provider_settings" --span (metadata $data_path).span) } if not ($data_path | path exists) { ^mkdir -p $data_path } let provider_path = ($data_path | path join $"($server.provider)_cache.((get-work-format))") let data = (load_provider_env $settings $server $provider_path) if ($data | is-empty) or ($data | get main?.vpc? | default null) == "?" { mw_create_cache $settings $server false (load_provider_env $settings $server $provider_path) } else { $data } } # Helper function: Load servers from definition files def load-servers-from-definitions [ servers_paths: list src_path: string wk_settings_path: string no_error: bool ] { mut loaded_servers = [] for it in $servers_paths { let file_path = if ($it | str ends-with ".ncl") { $it } else { $"($it).ncl" } let server_path = if ($file_path | str starts-with "/") { $file_path } else { ($src_path | path dirname | path join $file_path) } if not ($server_path | path exists) { if $no_error { "" | save $server_path } else { (throw-error "🛑 server path not found " ($server_path) "load-servers-from-definitions" --span (metadata $servers_paths).span) } continue } let target_settings_path = $"($wk_settings_path)/($it | str replace --all "/" "_").((get-work-format))" if not (parse_nickel_file ($server_path) $target_settings_path false "🛑 load settings failed ") { continue } if not ($target_settings_path | path exists) { continue } let servers_defs = (open $target_settings_path | default {}) let servers = ($servers_defs | get servers? | default []) $loaded_servers = ($loaded_servers | append $servers) } $loaded_servers } # Helper function: Process individual server with defaults and provider data def process-server [ server: record settings_data: record src_path: string src_dir: string wk_settings_path: string data_fullpath: string infra_path: string include_notuse: bool providers_settings: list ] { # Filter out servers with not_use=True when include_notuse is false if not $include_notuse and ($server | get not_use? | default false) { return { server: null providers_settings: $providers_settings } } let provider = $server.provider # Load provider defaults if not already loaded if not ($"($wk_settings_path)/($provider)($settings_data.settings.defaults_provs_suffix).((get-work-format))" | path exists) { let dflt_item = ($settings_data.settings.defaults_provs_dirpath | path join $"($provider)($settings_data.settings.defaults_provs_suffix)") let dflt_item_fullpath = if ($dflt_item | str starts-with ".") { ($src_dir | path join $dflt_item) } else { $dflt_item } load_defaults $src_path $dflt_item_fullpath ($wk_settings_path | path join $"($provider)($settings_data.settings.defaults_provs_suffix).((get-work-format))") } # Merge server with provider defaults let server_with_dflts = if ($"($wk_settings_path)/($provider)($settings_data.settings.defaults_provs_suffix).((get-work-format))" | path exists) { open ($"($wk_settings_path)/($provider)($settings_data.settings.defaults_provs_suffix).((get-work-format))") | merge $server } else { $server } # Load provider-level data settings let server_prov_data = if ($data_fullpath | path join $"($provider)($settings_data.settings.prov_data_suffix)" | path exists) { (load_defaults $src_dir ($data_fullpath | path join $"($provider)($settings_data.settings.prov_data_suffix)") ($wk_settings_path | path join $"($provider)($settings_data.settings.prov_data_suffix)") ) if (($wk_settings_path | path join $"($provider)($settings_data.settings.prov_data_suffix)") | path exists) { $server_with_dflts | merge (load_from_wk_format ($wk_settings_path | path join $"($provider)($settings_data.settings.prov_data_suffix)")) } else { $server_with_dflts } } else { $server_with_dflts } # Load server-specific data settings let server_with_data = if ($data_fullpath | path join $"($server.hostname)_($provider)($settings_data.settings.prov_data_suffix)" | path exists) { (load_defaults $src_dir ($data_fullpath | path join $"($server.hostname)_($provider)($settings_data.settings.prov_data_suffix)") ($wk_settings_path | path join $"($server.hostname)_($provider)($settings_data.settings.prov_data_suffix)") ) if ($wk_settings_path | path join $"($server.hostname)_($provider)($settings_data.settings.prov_data_suffix)" | path exists) { $server_prov_data | merge (load_from_wk_format ($wk_settings_path | path join $"($server.hostname)_($provider)($settings_data.settings.prov_data_suffix)")) } else { $server_prov_data } } else { $server_prov_data } # Load provider settings if not already loaded mut updated_providers = $providers_settings if ($providers_settings | where {|it| $it.provider == $provider} | length) == 0 { $updated_providers = ($updated_providers | append { provider: $provider, settings: (load_provider_settings { data: $settings_data, providers: $providers_settings, src: ($src_path | path basename), src_path: ($src_path | path dirname), infra: ($infra_path | path basename), infra_path: ($infra_path | path dirname), wk_path: $wk_settings_path } $server_with_data) }) } { server: $server_with_data providers_settings: $updated_providers } } export def load [ infra?: string in_src?: string include_notuse?: bool = false --no_error ] { let source = if $in_src == null or ($in_src | str ends-with '.ncl' ) { $in_src } else { $"($in_src).ncl" } # Try to determine the source path to load let source_path = if $source != null and ($source | path type) == "dir" { # If source is a directory, try main.ncl first (new pattern), then settings.ncl (legacy) let main_path = $"($source)/main.ncl" let settings_path = $"($source)/settings.ncl" if ($main_path | path exists) { $main_path } else if ($settings_path | path exists) { $settings_path } else { $source } } else { $source } let src_path = if $source_path != null and ($source_path | path exists) { $source_path } else if ($infra | is-not-empty) and (($infra | path join "main.ncl") | path exists) { $infra | path join "main.ncl" } else if ($infra | is-not-empty) and (($infra | path join (get-default-settings)) | path exists) { $infra | path join (get-default-settings) } else if ($infra | is-not-empty) { # Infra specified but files not found if $no_error { return {} } else { return } } else if ((get-default-settings) | path exists) { $"./((get-default-settings))" } else { # No source found - return empty record gracefully if $no_error { return {} } else { return } } let src_dir = ($src_path | path dirname) let infra_path = if $src_dir == "." { $env.PWD } else if ($src_dir | is-empty) { $env.PWD | path join $infra } else if ($src_dir | path exists) and ($src_dir | str starts-with "/") { $src_dir } else { $env.PWD | path join $src_dir } # Guard: Check source file exists if not ($src_path | path exists) { if $no_error { return {} } else { return } } # Convert to absolute path (handles both relative and absolute paths) let abs_path = ($src_path | path expand) # Cache not updated # let config = (ncl-eval $abs_path [] | from json) # print $config_p.servers.server_type let prov_root = ($env.PROVISIONING? | default "/usr/local/provisioning") let config = (ncl-eval $abs_path [$prov_root]) # Filter servers by include_notuse flag: keep only enabled servers let filtered_servers = ($config.servers | where { |s| (not ($s.not_use? | default false)) and ($s.enabled? | default true) }) # Return standardized settings structure (expected by provisioning CLI) { data: ($config | merge { servers: $filtered_servers }) providers: ($config.providers? | default []) src: ($src_path | path basename) src_path: ($src_path | path dirname) infra_path: $infra_path wk_path: (mktemp -d) } } export def load_settings [ --infra (-i): string --settings (-s): string # Settings path include_notuse: bool = false no_error: bool = false ] { let kld = get_infra (if $infra == null { "" } else { $infra }) if $no_error { (load $kld $settings $include_notuse --no_error) } else { (load $kld $settings $include_notuse) } # let settings = (load $kld $settings $exclude_not_use) # if $env.PROVISIONING_USE_SOPS? != "" { # use sops/lib.nu check_sops # check_sops $settings.src_path # } # $settings } export def save_settings_file [ settings: record target_file: string match_text: string new_text: string mark_changes: bool = false ] { let it_path = if ($target_file | path exists) { $target_file } else if ($settings.src_path | path join $"($target_file).ncl" | path exists) { ($settings.src_path | path join $"($target_file).ncl") } else if ($settings.src_path | path join $"($target_file).((get-work-format))" | path exists) { ($settings.src_path | path join $"($target_file).((get-work-format))") } else { _print $"($target_file) not found in ($settings.src_path)" return false } if (is_sops_file $it_path) { let result = (on_sops "decrypt" $it_path --quiet) if ($result | is-empty) { (throw-error $"🛑 saving settings to ($it_path)" $"from ($match_text) to ($new_text)" $"in ($target_file)" --span (metadata $it_path).span) return false } else { $result | str replace $match_text $new_text| save --force $it_path let en_result = (on_sops "encrypt" $it_path --quiet) if ($en_result | is-not-empty) { ($en_result | save --force $it_path) } } } else { open $it_path --raw | str replace $match_text $new_text | save --force $it_path } #if $it_path != "" and (^grep -q $match_text $it_path | complete).exit_code == 0 { # if (^sed -i $"s/($match_text)/($match_text)\"($new_text)\"/g" $it_path | complete).exit_code == 0 { _print $"($target_file) saved with new value " if $mark_changes { if ($settings.wk_path | path join "changes" | path exists) == false { $"($it_path) has been changed" | save ($settings.wk_path | path join "changes") --append } } else if ((get-provisioning-module) | is-not-empty) { ^(get-provisioning-name) "-mod" (get-provisioning-module) $env.PROVISIONING_ARGS exit } # } #} } export def save_servers_settings [ settings: record match_text: string new_text: string ] { $settings.data.servers_paths | each { | it | save_settings_file $settings $it $match_text $new_text } } export def settings_with_env [ settings: record ] { mut $servers_with_ips = [] for srv in ($settings.data.servers) { let pub_ip = (mw_ip_from_cache $settings $srv false) if ($pub_ip | is-empty) { $servers_with_ips = ($servers_with_ips | append ($srv)) } else { $servers_with_ips = ($servers_with_ips | append ($srv | merge { network_public_ip: $pub_ip })) } } ($settings | merge { data: ($settings.data | merge { servers: $servers_with_ips}) }) }