# Dynamic Cache Configuration Manager # Manages cache settings at runtime via persistent JSON config # Follows Nushell 0.109.0+ guidelines # Helper: Get runtime config file path def get-runtime-config-path [] { let home = ($env.HOME? | default "~" | path expand) $home | path join ".provisioning" "cache" "config" "settings.json" } # Helper: Get system defaults path def get-defaults-config-path [] { let provisioning = ($env.PROVISIONING? | default "/usr/local/provisioning") $provisioning | path join "config" "config.defaults.toml" } # Helper: Load runtime config file def load-runtime-config [] { let config_path = (get-runtime-config-path) if ($config_path | path exists) { let load_result = (do { open $config_path } | complete) if $load_result.exit_code == 0 { return $load_result.stdout } } # Return empty record if not exists {} } # Helper: Save runtime config file def save-runtime-config [ config: record ] { let config_path = (get-runtime-config-path) let config_dir = ($config_path | path dirname) # Ensure directory exists if not ($config_dir | path exists) { mkdir $config_dir } # Save with pretty formatting do { $config | to json --indent 2 | save --force $config_path } | complete | ignore } # ============================================================================ # PUBLIC API: Configuration Management # ============================================================================ # Get complete cache configuration (merged: runtime overrides + defaults) export def get-cache-config [] { let defaults = { enabled: true, max_cache_size: 104857600, # 100 MB ttl: { final_config: 300, # 5 minutes nickel_compilation: 1800, # 30 minutes sops_decryption: 900, # 15 minutes provider_config: 600, # 10 minutes platform_config: 600 # 10 minutes }, paths: { base: (($env.HOME? | default "~" | path expand) | path join ".provisioning" "cache" "config") }, security: { sops_file_permissions: "0600", sops_dir_permissions: "0700" }, validation: { strict_mtime: true }, metadata: { last_modified: (date now | format date "%Y-%m-%dT%H:%M:%SZ"), modified_by: "system", version: "1.0" } } # Load runtime overrides let runtime = (load-runtime-config) # Merge: runtime overrides take precedence $defaults | merge $runtime } # Get specific cache setting (dot notation: "ttl.final_config") export def cache-config-get [ setting_path: string # "enabled", "ttl.final_config", etc. ] { let config = (get-cache-config) let result = (do { eval $"$config | get ($setting_path)" } | complete) if $result.exit_code == 0 { $result.stdout } else { null } } # Set cache configuration (persists to runtime config) export def cache-config-set [ setting_path: string value: any ] { let runtime = (load-runtime-config) # Build nested structure from dot notation mut updated = $runtime # Simple update for direct keys if not ($setting_path | str contains ".") { $updated = ($updated | insert $setting_path $value) } else { # For nested paths, we need to handle carefully # Convert "ttl.final_config" -> insert into ttl section let parts = ($setting_path | split row ".") if ($parts | length) == 2 { let section = ($parts | get 0) let key = ($parts | get 1) let section_obj = if ($runtime | has -c $section) { $runtime | get $section } else { {} } let updated_section = ($section_obj | insert $key $value) $updated = ($updated | insert $section $updated_section) } } # Add metadata $updated = ($updated | insert "metadata" { last_modified: (date now | format date "%Y-%m-%dT%H:%M:%SZ"), modified_by: "user", version: "1.0" }) save-runtime-config $updated } # Reset cache configuration (delete runtime overrides) export def cache-config-reset [ setting_path?: string = "" # Optional: reset specific setting ] { if ($setting_path | is-empty) { # Delete entire runtime config file let config_path = (get-runtime-config-path) if ($config_path | path exists) { do { rm -f $config_path } | complete | ignore } } else { # Remove specific setting let runtime = (load-runtime-config) mut updated = $runtime # Handle nested paths if not ($setting_path | str contains ".") { if ($updated | has -c $setting_path) { $updated = ($updated | del $setting_path) } } else { let parts = ($setting_path | split row ".") if ($parts | length) == 2 { let section = ($parts | get 0) let key = ($parts | get 1) if ($updated | has -c $section) { let section_obj = ($updated | get $section) let updated_section = (if ($section_obj | has -c $key) { $section_obj | del $key } else { $section_obj }) $updated = ($updated | insert $section $updated_section) } } } save-runtime-config $updated } } # Show cache configuration in formatted output export def cache-config-show [ --format: string = "yaml" # "yaml", "json", "table" ] { let config = (get-cache-config) match $format { "json" => { $config | to json --indent 2 | print }, "table" => { $config | print }, _ => { $config | to yaml | print } } } # Validate cache configuration export def cache-config-validate [] { let config = (get-cache-config) mut errors = [] mut warnings = [] # Validate enabled field if not ($config | has -c "enabled") { $errors = ($errors | append "Missing 'enabled' field") } # Validate TTL values (should be positive integers) if ($config | has -c "ttl") { for ttl_key in [ "final_config" "nickel_compilation" "sops_decryption" "provider_config" "platform_config" ] { let ttl_value = ($config.ttl | get --optional $ttl_key | default 0) if ($ttl_value <= 0) { $warnings = ($warnings | append $"TTL ($ttl_key) is not positive: ($ttl_value)") } } } # Validate max cache size (should be reasonable) if ($config | has -c "max_cache_size") { let max_size = $config.max_cache_size if ($max_size < 1048576) { # Less than 1 MB $warnings = ($warnings | append $"max_cache_size is very small: ($max_size) bytes") } if ($max_size > 10737418240) { # More than 10 GB $warnings = ($warnings | append $"max_cache_size is very large: ($max_size) bytes") } } { valid: (($errors | length) == 0), errors: $errors, warnings: $warnings } } # Get cache statistics along with configuration export def get-cache-status [] { use ../../cache/core.nu get-cache-stats let config = (get-cache-config) let stats = (get-cache-stats) { configuration: $config, statistics: $stats, enabled: $config.enabled, max_size_mb: ($config.max_cache_size / 1048576), current_usage_percent: ( if ($stats.total_size_mb | is-not-empty) { ($stats.total_size_mb / ($config.max_cache_size / 1048576) * 100) | math round -p 1 } else { 0 } ) } } # Export configuration to portable format export def cache-config-export [ --format: string = "json" ] { let config = (get-cache-config) match $format { "json" => { $config | to json --indent 2 }, "yaml" => { $config | to yaml }, "toml" => { # Basic TOML conversion (limited) $"[cache] enabled = ($config.enabled) max_cache_size = ($config.max_cache_size) " }, _ => { $config | to json --indent 2 } } } # Import configuration from JSON string export def cache-config-import [ json_string: string ] { let result = (do { $json_string | from json } | complete) if $result.exit_code == 0 { save-runtime-config $result.stdout } else { error make { msg: "Failed to parse configuration JSON" } } } # Get configuration defaults (system-wide) export def get-cache-defaults [] { { enabled: true, max_cache_size: 104857600, # 100 MB ttl: { final_config: 300, nickel_compilation: 1800, sops_decryption: 900, provider_config: 600, platform_config: 600 }, paths: { base: (($env.HOME? | default "~" | path expand) | path join ".provisioning" "cache" "config") }, security: { sops_file_permissions: "0600", sops_dir_permissions: "0700" }, validation: { strict_mtime: true } } } # Restore default configuration export def cache-config-restore-defaults [] { let config_path = (get-runtime-config-path) if ($config_path | path exists) { do { rm -f $config_path } | complete | ignore } }