#!/usr/bin/env nu use std log # Helper: Determine platform configuration base directory # Uses PROVISIONING_USER_PLATFORM env var if set, otherwise defaults to OS-specific path def get-platform-base-dir [] { let env_path = ($env.PROVISIONING_USER_PLATFORM? | default "") if ($env_path | is-empty) { # Fallback to OS-specific default location let home = $nu.home-dir let os = $nu.os-info.name if $os == "macos" { $"($home)/Library/Application Support/provisioning/platform" } else { # Linux and other Unix-like systems $"($home)/.config/provisioning/platform" } } else { $env_path } } # Display help information def show-help [] { let home = $nu.home-dir let os = $nu.os-info.name let macos_config = $"($home)/Library/Application Support/provisioning/platform" let linux_config = $"($home)/.config/provisioning/platform" let default_config = if $os == "macos" { $macos_config } else { $linux_config } print "╭─ PROVISIONING MANIFEST GENERATOR ──────────────────────────────────────" print "│" print "│ Generate Kubernetes and Docker Compose manifests from Nickel templates" print "│" print "├─ PLATFORM-SPECIFIC PATHS ─────────────────────────────────────────────" print "│" print "│ macOS:" print "│ Config Source: ~/Library/Application Support/provisioning/platform/config/" print "│ Output Directory: ~/Library/Application Support/provisioning/platform/" print "│" print "│ Linux:" print "│ Config Source: ~/.config/provisioning/platform/config/" print "│ Output Directory: ~/.config/provisioning/platform/" print "│" print "├─ USAGE ───────────────────────────────────────────────────────────────" print "│" print "│ Get help:" print "│ nu scripts/platform_generate-manifests.nu help" print "│ nu scripts/platform_generate-manifests.nu -h (shows Nushell built-in help)" print "│" print "│ Generate both Docker and Kubernetes (default):" print "│ nu scripts/platform_generate-manifests.nu" print "│" print "│ Generate specific target:" print "│ nu scripts/platform_generate-manifests.nu docker # Docker Compose only" print "│ nu scripts/platform_generate-manifests.nu kubernetes # Kubernetes only" print "│ nu scripts/platform_generate-manifests.nu all # Both (same as no args)" print "│" print "│ With validation:" print "│ nu scripts/platform_generate-manifests.nu docker --validate" print "│ nu scripts/platform_generate-manifests.nu all --validate" print "│" print "│ Custom paths:" print "│ nu scripts/platform_generate-manifests.nu kubernetes \\" print "│ --config-dir /path/to/configs \\" print "│ --output-dir /path/to/output" print "│" print "├─ OPTIONS ─────────────────────────────────────────────────────────────" print "│" print "│ TARGET (optional):" print "│ docker Generate Docker Compose manifests only" print "│ kubernetes (or k8s) Generate Kubernetes manifests only" print "│ all Generate both Docker and Kubernetes" print "│ (none) Generate both (default behavior)" print "│" print "│ FLAGS:" print "│ help Show this help message" print "│ -h Show Nushell built-in help" print "│ --validate Validate generated manifests (Docker, Kubernetes)" print "│ --config-dir Source directory for user configs" print $"│ Default: ($default_config)/config/" print "│" print "│ --output-dir Destination directory for manifests" print $"│ Default: ($default_config)/" print "│" print "├─ ENVIRONMENT VARIABLES ────────────────────────────────────────────────" print "│" print "│ PROVISIONING Provisioning repository root" print "│ Default: Current working directory" print "│" print "├─ OUTPUT ──────────────────────────────────────────────────────────────" print "│" print "│ Generated Files:" print "│ • docker-compose.yml Docker Compose stack configuration" print "│ • k8s/ Kubernetes manifests directory" print "│ ├─ namespace.yaml" print "│ ├─ resource-quota.yaml" print "│ ├─ rbac.yaml" print "│ ├─ network-policy.yaml" print "│ ├─ orchestrator-*.yaml" print "│ ├─ control-center-*.yaml" print "│ ├─ mcp-server-*.yaml" print "│ └─ platform-ingress.yaml" print "│" print "├─ EXAMPLES ─────────────────────────────────────────────────────────────" print "│" print "│ 1. Generate both Docker and Kubernetes (default):" print "│ nu scripts/platform_generate-manifests.nu" print "│" print "│ 2. Generate Docker Compose only:" print "│ nu scripts/platform_generate-manifests.nu docker" print "│" print "│ 3. Generate Kubernetes manifests only:" print "│ nu scripts/platform_generate-manifests.nu kubernetes" print "│" print "│ 4. Generate both with validation:" print "│ nu scripts/platform_generate-manifests.nu all --validate" print "│" print "│ 5. Docker Compose with custom output directory:" print "│ nu scripts/platform_generate-manifests.nu docker \\" print "│ --output-dir /tmp/manifests" print "│" print "│ 6. Kubernetes with custom config and output directories:" print "│ nu scripts/platform_generate-manifests.nu kubernetes \\" print "│ --config-dir ~/.provisioning-custom/config \\" print "│ --output-dir /tmp/k8s-manifests \\" print "│ --validate" print "│" print "├─ REQUIREMENTS ─────────────────────────────────────────────────────────" print "│" print "│ • Nushell 0.110.0+" print "│ • Nickel CLI" print "│ • PROVISIONING environment variable or current directory as repo root" print "│" print "│ Optional (for validation):" print "│ • Docker and Docker Compose (for docker-compose validation)" print "│ • kubectl (for Kubernetes validation)" print "│" print "├─ TROUBLESHOOTING ──────────────────────────────────────────────────────" print "│" print "│ Config directory not found?" print "│ → Check that you have user configs in the default location" print "│ → Or use --config-dir to specify a custom path" print "│" print "│ Docker/Kubernetes validation failing?" print "│ → Ensure Docker and kubectl are installed" print "│ → Run without --validate flag to skip validation" print "│" print "│ Template rendering errors?" print "│ → Verify PROVISIONING environment variable is set:" print "│ export PROVISIONING=/path/to/provisioning" print "│ → Check that templates exist in:" print "│ \$PROVISIONING/schemas/platform/templates/" print "│" print "╰────────────────────────────────────────────────────────────────────────" } def generate-manifests [ target?: string # What to generate: "docker" (docker-compose only), "kubernetes" (k8s only), or "all" (both) --validate = false # Validate generated manifests --output-dir: string # Output directory for manifests --config-dir: string # Config directory with user service configs ] { # Use PROVISIONING environment variable for repo root, fallback to current directory let provisioning_root = $env.PROVISIONING? | default (pwd) # Determine output directory (where manifests are written) let platform_output_dir = if ($output_dir == null or ($output_dir | is-empty)) { get-platform-base-dir } else { $output_dir } # Determine config directory (where user configs are read from) let platform_config_dir = if ($config_dir == null or ($config_dir | is-empty)) { get-platform-base-dir } else { $config_dir } # Templates are now in schemas/platform/templates/ let templates_dir = $"($provisioning_root)/schemas/platform/templates" # NICKEL_IMPORT_PATH: provisioning root first (for schemas), then user config directory let nickel_import_path = $"($provisioning_root):($platform_config_dir)/config" # Determine what to generate let generate_target = if ($target == null or ($target | is-empty)) { # No argument provided - default to generating both "all" } else { # Validate provided argument let t = ($target | str downcase) if ($t == "docker" or $t == "k8s" or $t == "kubernetes" or $t == "all") { if $t == "k8s" { "kubernetes" } else { $t } } else { log error "Invalid target: $target" log error "Valid options: docker, kubernetes (or k8s), all" error make { msg: "Invalid target specified" } } } log info "🔧 Generating manifests from Nickel templates..." log info $" Target: ($generate_target)" log info $" Provisioning repo: ($provisioning_root)" log info $" Config source: ($platform_config_dir)/config/" log info $" Output destination: ($platform_output_dir)" # Create output directory if it doesn't exist if not ($platform_output_dir | path exists) { ^mkdir -p $platform_output_dir log info $"📁 Created output directory: ($platform_output_dir)" } # Check for user config files to inform build context if ($platform_config_dir | path exists) { let config_files = (do { ^find $"($platform_config_dir)/config" -name "*.ncl" -type f 2>/dev/null } | complete).stdout | lines | where { $in != "" } let config_count = $config_files | length if $config_count > 0 { log info $"📋 Found ($config_count) service config files:" $config_files | each { |f| let relative = $f | str replace $"($platform_config_dir)/" "" log info $" • ($relative)" } } } else { log warning $"⚠️ Config directory not found: ($platform_config_dir)/config" log info " Manifests will be generated from templates only" } # Generate docker-compose (if requested) if ($generate_target == "docker" or $generate_target == "all") { log info "📦 Generating docker-compose.yml..." let template = "schemas/platform/templates/docker-compose/platform-stack.solo.yml.ncl" let nickel_path = $"($platform_config_dir)/config:($provisioning_root):($provisioning_root)/schemas/platform" let dc_result = (do { with-env { NICKEL_IMPORT_PATH: $nickel_path } { cd $provisioning_root ^nickel export --format yaml $template } } | complete) if $dc_result.exit_code == 0 { $dc_result.stdout | save --force $"($platform_output_dir)/docker-compose.yml" log info "✅ docker-compose.yml generated" } else { log error $"Failed to generate docker-compose.yml: ($dc_result.stderr)" error make { msg: "Docker Compose generation failed" } } } # Generate Kubernetes manifests (if requested) if ($generate_target == "kubernetes" or $generate_target == "all") { let k8s_dir = $"($platform_output_dir)/k8s" if not ($k8s_dir | path exists) { ^mkdir -p $k8s_dir } # Kubernetes templates available in new location let k8s_templates = [ "namespace", "resource-quota", "rbac", "network-policy", "orchestrator-deployment", "orchestrator-service", "control-center-deployment", "control-center-service", "mcp-server-deployment", "mcp-server-service", "platform-ingress", ] let nickel_path = $"($platform_config_dir)/config:($provisioning_root):($provisioning_root)/schemas/platform" for template in $k8s_templates { log info $"☸️ Generating ($template).yaml..." let template_path = $"schemas/platform/templates/kubernetes/($template).yaml.ncl" let k8s_result = (do { with-env { NICKEL_IMPORT_PATH: $nickel_path } { cd $provisioning_root ^nickel export --format yaml $template_path } } | complete) if $k8s_result.exit_code == 0 { $k8s_result.stdout | save --force $"($k8s_dir)/($template).yaml" log info $"✅ ($template).yaml generated" } else { log error $"Failed to generate ($template).yaml: ($k8s_result.stderr)" if not $validate { # Only fail on missing templates if not in validate-only mode continue } } } } if $validate { log info "🔍 Validating manifests..." if ($generate_target == "docker" or $generate_target == "all") { validate-compose $"($platform_output_dir)/docker-compose.yml" } if ($generate_target == "kubernetes" or $generate_target == "all") { let k8s_dir = $"($platform_output_dir)/k8s" validate-kubernetes $k8s_dir } } log info "✨ Generation complete!" log info $"📂 Output: ($platform_output_dir)" } def validate-compose [path: string] { log info "Validating Docker Compose..." let docker_check = (do { ^docker --version } | complete) if $docker_check.exit_code != 0 { log warning "⚠️ Docker not found - skipping validation" return } let compose_check = (do { ^docker compose version } | complete) if $compose_check.exit_code != 0 { log warning "⚠️ Docker Compose not available - skipping validation" return } let result = (do { ^docker compose -f $path config } | complete) if $result.exit_code == 0 { let service_count = ($result.stdout | lines | where { $in | str contains "container_name:" } | length) log info $"✅ docker-compose.yml is valid" log info $" Services: ($service_count)" } else { log error "❌ Docker Compose validation failed" log error $result.stderr error make { msg: "Docker Compose validation error" } } } def validate-kubernetes [k8s_dir: string] { log info "Validating Kubernetes manifests..." let kubectl_check = (do { ^kubectl version --client } | complete) if $kubectl_check.exit_code != 0 { log warning "⚠️ kubectl not found - skipping validation" return } let result = (do { ^kubectl apply --dry-run=client -f $k8s_dir } | complete) if $result.exit_code == 0 { let yaml_files = (do { ^find $k8s_dir -name "*.yaml" -type f } | complete).stdout | lines | where { $in != "" } | length log info "✅ Kubernetes manifests are valid" log info $" Manifest files: ($yaml_files)" } else { if ($result.stderr | str contains "connection refused") { log warning "⚠️ Cannot reach Kubernetes cluster - syntax validation only" } else { log error "❌ Kubernetes validation failed" log error $result.stderr error make { msg: "Kubernetes validation error" } } } } # Entry point def main [ target?: string # What to generate: 'docker', 'kubernetes' (or 'k8s'), 'all', or 'help' --validate = false # Validate generated manifests --output-dir: string # Output directory for manifests --config-dir: string # Config directory with user service configs ] { # Check if help was requested if ($target == "help") or ($target == "-h") or ($target == "--h") { show-help return } # Delegate to generate-manifests generate-manifests $target --validate=$validate --output-dir=$output_dir --config-dir=$config_dir }