prvng_core/nulib/lib_provisioning/module_loader.nu
Jesús Pérez eb20fec7de
chore: release 1.0.11 - nu script cleanup & refactoring + i18n fluentd
- Documented Fluent-based i18n system with locale detection
  - Bumped version from 1.0.10 to 1.0.11
2026-01-14 02:00:23 +00:00

372 lines
12 KiB
Plaintext

# Nickel Module Loader Library
# Provides functions for discovering, syncing, and managing Nickel modules
# Used by CLI commands and other components
# Author: JesusPerezLorenzo
# Date: 2025-09-29
use config/accessor.nu *
use config/cache/simple-cache.nu *
use utils *
# Discover Nickel modules from extensions (providers, taskservs, clusters)
export def "discover-nickel-modules" [
type: string # "providers" | "taskservs" | "clusters"
] {
# Fast path: don't load config, just use extensions path directly
# This avoids Nickel evaluation which can hang the system
let proj_root = ($env.PROVISIONING_ROOT? | default "/Users/Akasha/project-provisioning")
let base_path = ($proj_root | path join "provisioning" "extensions" $type)
if not ($base_path | path exists) {
return []
}
# Discover modules using directory structure
# Use proper Nushell ls with null stdin to avoid hanging
let modules = (ls $base_path
| where type == "dir"
| get name
| path basename)
# Build table with Nickel information
$modules | each {|module_name|
let module_path = ($base_path | path join $module_name)
let schema_path = ($module_path | path join "nickel")
# Check if Nickel directory exists
if not ($schema_path | path exists) {
return null
}
# Read nickel.mod for metadata
let mod_path = ($schema_path | path join "nickel.mod")
let metadata = if ($mod_path | path exists) {
parse-nickel-mod $mod_path
} else {
{name: "", version: "0.0.1", edition: "v0.11.3"}
}
# Determine Nickel module name based on type
let module_name = match $type {
"providers" => $"($module_name)_prov"
"taskservs" => $"($module_name)_task"
"clusters" => $"($module_name)_cluster"
_ => $module_name
}
{
name: $module_name
type: $type
path: $module_path
schema_path: $schema_path
module_name: $module_name
version: $metadata.version
edition: $metadata.edition
has_nickel: true
}
} | compact
}
# Cached version of discover-nickel-modules
# NOTE: In practice, OS filesystem caching (dentry cache, inode cache) is more efficient
# than custom caching due to Nushell's JSON serialization overhead.
# This function is provided for future optimization when needed.
export def "discover-nickel-modules-cached" [
type: string # "providers" | "taskservs" | "clusters"
] {
# Direct call - relies on OS filesystem cache for performance
discover-nickel-modules $type
}
# Parse nickel.mod file and extract metadata
def "parse-nickel-mod" [
mod_path: string
] {
let content = (open $mod_path)
# Simple TOML parsing for [package] section
let lines = ($content | lines)
mut name = ""
mut version = "0.0.1"
mut edition = "v0.11.3"
for line in $lines {
if ($line | str starts-with "name") {
$name = ($line | parse "name = \"{name}\"" | get name.0? | default "")
} else if ($line | str starts-with "version") {
$version = ($line | parse "version = \"{version}\"" | get version.0? | default "0.0.1")
} else if ($line | str starts-with "edition") {
$edition = ($line | parse "edition = \"{edition}\"" | get edition.0? | default "v0.11.3")
}
}
{name: $name, version: $version, edition: $edition}
}
# Sync Nickel dependencies for an infrastructure workspace
export def "sync-nickel-dependencies" [
infra_path: string
--manifest: string = "providers.manifest.yaml"
] {
let manifest_path = ($infra_path | path join $manifest)
if not ($manifest_path | path exists) {
error make {msg: $"Manifest not found: ($manifest_path)"}
}
let manifest = (open $manifest_path)
let modules_dir_name = (config-get "nickel.modules_dir" "nickel")
let modules_dir = ($infra_path | path join $modules_dir_name)
# Create modules directory if it doesn't exist
mkdir $modules_dir
_print $"🔄 Syncing Nickel dependencies for ($infra_path | path basename)..."
# Sync each provider from manifest
if ($manifest | get providers? | is-not-empty) {
$manifest.providers | each {|provider|
sync-provider-module $provider $modules_dir
}
}
# Update nickel.mod
update-nickel-mod $infra_path $manifest
_print "✅ Nickel dependencies synced successfully"
}
# Sync a single provider module (create symlink)
def "sync-provider-module" [
provider: record
modules_dir: string
] {
let discovered = (discover-nickel-modules-cached "providers"
| where name == $provider.name)
if ($discovered | is-empty) {
error make {msg: $"Provider '($provider.name)' not found in extensions/providers"}
}
let module_info = ($discovered | first)
let link_path = ($modules_dir | path join $module_info.module_name)
# Remove existing symlink if present
if ($link_path | path exists) {
rm -f $link_path
}
# Create symlink (relative path for portability)
let relative_path = (get-relative-path $modules_dir $module_info.schema_path)
# Use ln -sf for symlink
^ln -sf $relative_path $link_path
_print $" ✓ ($provider.name) → ($link_path | path basename)"
}
# Get relative path from source to target
def "get-relative-path" [
from: string
to: string
] {
# Calculate relative path
# For now, use absolute path (Nickel handles this fine)
$to
}
# Update nickel.mod with provider dependencies
export def "update-nickel-mod" [
infra_path: string
manifest: record
] {
let mod_path = ($infra_path | path join "nickel.mod")
if not ($mod_path | path exists) {
error make {msg: $"nickel.mod not found at ($mod_path)"}
}
let current_mod = (open $mod_path)
let modules_dir_name = (get-config | get nickel.modules_dir)
# Generate provider dependencies
let provider_deps = if ($manifest | get providers? | is-not-empty) {
# Load all providers once to cache them
let all_providers = (discover-nickel-modules-cached "providers")
$manifest.providers | each {|provider|
let discovered = ($all_providers | where name == $provider.name)
if ($discovered | is-empty) {
return ""
}
let module_info = ($discovered | first)
$"($module_info.module_name) = { path = \"./($modules_dir_name)/($module_info.module_name)\", version = \"($provider.version)\" }"
} | str join "\n"
} else {
""
}
# Parse current nickel.mod and update dependencies section
let lines = ($current_mod | lines)
mut in_deps = false
mut new_lines = []
mut deps_section = []
for line in $lines {
if ($line | str trim | str starts-with "[dependencies]") {
$in_deps = true
$new_lines = ($new_lines | append $line)
continue
}
if $in_deps {
if ($line | str trim | str starts-with "[") {
# End of dependencies section
# Add provider deps before closing
if ($provider_deps | str length) > 0 {
for dep_line in ($provider_deps | lines) {
$new_lines = ($new_lines | append $dep_line)
}
}
$in_deps = false
$new_lines = ($new_lines | append $line)
} else if (($line | str trim | str length) > 0) and not (($line | str contains "_prov") or ($line | str contains "_task") or ($line | str contains "_cluster")) {
# Keep non-module-loader dependencies (like provisioning core)
$new_lines = ($new_lines | append $line)
}
} else {
$new_lines = ($new_lines | append $line)
}
}
# If we're still in deps section at end, add provider deps
if $in_deps and (($provider_deps | str length) > 0) {
for dep_line in ($provider_deps | lines) {
$new_lines = ($new_lines | append $dep_line)
}
}
# Write updated nickel.mod
$new_lines | str join "\n" | save -f $mod_path
_print $" ✓ Updated nickel.mod with provider dependencies"
}
# Install a provider to an infrastructure
export def "install-provider" [
provider_name: string
infra_path: string
--version: string = "0.0.1"
] {
# Discover provider using cached version
let available = (discover-nickel-modules-cached "providers" | where name == $provider_name)
if ($available | is-empty) {
error make {msg: $"Provider '($provider_name)' not found"}
}
let provider_info = ($available | first)
_print $"📦 Installing provider ($provider_name) for ($infra_path | path basename)..."
# Update or create manifest
update-manifest $infra_path $provider_name $version
# Sync Nickel dependencies
sync-nickel-dependencies $infra_path
_print $"✅ Provider ($provider_name) installed successfully"
}
# Update providers.manifest.yaml with new provider
def "update-manifest" [
infra_path: string
provider_name: string
version: string
] {
let manifest_path = ($infra_path | path join "providers.manifest.yaml")
let manifest = if ($manifest_path | path exists) {
open $manifest_path
} else {
{providers: []}
}
# Check if provider already exists
let existing_providers = ($manifest | get providers)
let provider_exists = ($existing_providers | any {|p| $p.name == $provider_name})
let updated_providers = if $provider_exists {
# Update version
$existing_providers | each {|p|
if $p.name == $provider_name {
{name: $provider_name, version: $version, enabled: true}
} else {
$p
}
}
} else {
# Add new provider
$existing_providers | append {name: $provider_name, version: $version, enabled: true}
}
let updated_manifest = ($manifest | upsert providers $updated_providers)
$updated_manifest | to yaml | save -f $manifest_path
_print $" ✓ Updated providers.manifest.yaml"
}
# Remove a provider from an infrastructure
export def "remove-provider" [
provider_name: string
infra_path: string
] {
_print $"🗑️ Removing provider ($provider_name) from ($infra_path | path basename)..."
let manifest_path = ($infra_path | path join "providers.manifest.yaml")
if not ($manifest_path | path exists) {
error make {msg: "providers.manifest.yaml not found"}
}
let manifest = (open $manifest_path)
let updated_providers = ($manifest.providers | where name != $provider_name)
let updated_manifest = ($manifest | upsert providers $updated_providers)
$updated_manifest | to yaml | save -f $manifest_path
# Remove symlink
let modules_dir_name = (get-config | get nickel.modules_dir)
let modules_dir = ($infra_path | path join $modules_dir_name)
let discovered = (discover-nickel-modules-cached "providers" | where name == $provider_name)
if not ($discovered | is-empty) {
let module_info = ($discovered | first)
let link_path = ($modules_dir | path join $module_info.module_name)
if ($link_path | path exists) {
rm -f $link_path
_print $" ✓ Removed symlink ($link_path | path basename)"
}
}
# Sync to update nickel.mod
sync-nickel-dependencies $infra_path
_print $"✅ Provider ($provider_name) removed successfully"
}
# List all available Nickel modules
export def "list-nickel-modules" [
type: string # "providers" | "taskservs" | "clusters" | "all"
] {
if $type == "all" {
let providers = (discover-nickel-modules-cached "providers" | insert module_type "provider")
let taskservs = (discover-nickel-modules-cached "taskservs" | insert module_type "taskserv")
let clusters = (discover-nickel-modules-cached "clusters" | insert module_type "cluster")
$providers | append $taskservs | append $clusters
} else {
discover-nickel-modules-cached $type | insert module_type $type
}
}