#!/usr/bin/env nu # Taskserv version extraction and management utilities # Handles Nickel taskserv files and version configuration # Selective imports (ADR-025 Phase 3 Layer 2). # version/core.nu and version/loader.nu star-imports were dead — dropped. use primitives/io/interface.nu [_print] use platform/config/accessor/functions.nu [get-taskservs-path] # Extract version field from Nickel taskserv files export def extract-nickel-version [ file_path: string ] { if not ($file_path | path exists) { return "" } let content = (open $file_path --raw) # Look for version assignment in taskserv configuration files let version_matches = ($content | lines | each { |line| let trimmed_line = ($line | str trim) # Match "version = " pattern (but not major_version, cni_version, etc.) if ($trimmed_line | str starts-with "version") and ($trimmed_line | str contains "=") { # Split on equals and take the right side let parts = ($trimmed_line | split row "=") if ($parts | length) >= 2 { let version_value = ($parts | get 1 | str trim) if ($version_value | str starts-with '"') { # Remove quotes and get the value ($version_value | parse -r '"([^"]*)"' | get 0? | default {} | get capture0? | default "") } else if ($version_value | str starts-with "'") { # Handle single quotes ($version_value | parse -r "'([^']*)'" | get 0? | default {} | get capture0? | default "") } else { # Handle unquoted values (remove any trailing comments) ($version_value | str replace "\\s*#.*$" "" | str trim) } } else { "" } } else if ($trimmed_line | str starts-with "version:") and not ($trimmed_line | str contains "str") { # Handle schema-style "version: value" (not type declarations) let version_part = ($trimmed_line | str replace "version:\\s*" "") if ($version_part | str starts-with '"') { ($version_part | parse -r '"([^"]*)"' | get 0? | default {} | get capture0? | default "") } else if ($version_part | str starts-with "'") { ($version_part | parse -r "'([^']*)'" | get 0? | default {} | get capture0? | default "") } else { ($version_part | str replace "\\s*#.*$" "" | str trim) } } else { "" } } | where { |v| $v != "" }) if ($version_matches | length) > 0 { $version_matches | get 0 } else { "" } } # Discover all taskserv Nickel files and their versions export def discover-taskserv-configurations [ --base-path: string = "" ] { let taskservs_path = if ($base_path | is-not-empty) { $base_path } else { (get-taskservs-path) } if not ($taskservs_path | path exists) { _print $"⚠️ Taskservs path not found: ($taskservs_path)" return [] } # Find all .ncl files recursively in the taskservs directory let all_k_files = (glob $"($taskservs_path)/**/*.ncl") let nickel_configs = ($all_k_files | each { |decl_file| let version = (extract-nickel-version $decl_file) if ($version | is-not-empty) { let relative_path = ($decl_file | str replace $"($taskservs_path)/" "") let path_parts = ($relative_path | split row "/" | where { |p| $p != "" }) # Determine ID from the path structure let id = if ($path_parts | length) >= 2 { # If it's a server-specific file like "wuji-strg-1/kubernetes.ncl" let filename = ($decl_file | path basename | str replace ".ncl" "") $"($path_parts.0)::($filename)" } else { # If it's a general file like "proxy.ncl" ($decl_file | path basename | str replace ".ncl" "") } { id: $id type: "taskserv" nickel_file: $decl_file version: $version metadata: { source_file: $decl_file category: "taskserv" path_structure: $path_parts } } } else { null } } | where { |item| $item != null }) $nickel_configs } # Update version in Nickel file export def update-nickel-version [ file_path: string new_version: string ] { if not ($file_path | path exists) { _print $"❌ File not found: ($file_path)" return } let content = (open $file_path --raw) # Replace version field while preserving formatting let updated_content = ($content | lines | each { |line| if ($line | str trim | str starts-with "version:") { # Preserve indentation and update version let indent = ($line | str replace "^(\\s*).*" '$1') let line_trimmed = ($line | str trim) if ($line_trimmed | str contains '"') { $"($indent)version: \"($new_version)\"" } else if ($line_trimmed | str contains "'") { $"($indent)version: '($new_version)'" } else { $"($indent)version: str = \"($new_version)\"" } } else { $line } } | str join "\n") $updated_content | save -f $file_path _print $"✅ Updated version in ($file_path) to ($new_version)" } # Check taskserv versions against available versions export def check-taskserv-versions [ --fetch-latest = false ] { let configs = (discover-taskserv-configurations) if ($configs | is-empty) { _print "No taskserv configurations found" return [] } $configs | each { |config| # For now, return basic info - can be extended with version checking logic { id: $config.id type: $config.type configured: $config.version nickel_file: $config.nickel_file status: "configured" } } } # Update taskserv version in Nickel file export def update-taskserv-version [ taskserv_id: string new_version: string --dry-run = false ] { let configs = (discover-taskserv-configurations) let config = ($configs | where id == $taskserv_id | first | default null) if ($config | is-empty) { _print $"❌ Taskserv '($taskserv_id)' not found" return } if $dry_run { _print $"🔍 Would update ($taskserv_id) from ($config.version) to ($new_version) in ($config.nickel_file)" return } update-nickel-version $config.nickel_file $new_version } # Bulk update multiple taskservs export def bulk-update-taskservs [ updates: list # List of {id: string, version: string} --dry-run = false ] { if ($updates | is-empty) { _print "No updates provided" return } _print $"Updating ($updates | length) taskservs..." for update in $updates { let taskserv_id = ($update | get id? | default "") let new_version = ($update | get version? | default "") if ($taskserv_id | is-empty) or ($new_version | is-empty) { _print $"⚠️ Invalid update entry: ($update)" continue } update-taskserv-version $taskserv_id $new_version --dry-run=$dry_run } if not $dry_run { _print "✅ Bulk update completed" } } # Sync taskserv versions with registry export def taskserv-sync-versions [ --taskservs-path: string = "" --component: string = "" # Specific component to sync --dry-run = false ] { let registry = (load-version-registry) let comparisons = (compare-registry-with-taskservs --taskservs-path=$taskservs_path) if ($comparisons | is-empty) { _print "❌ No taskserv configurations found" return } # Filter to out-of-sync components mut out_of_sync = ($comparisons | where status == "out_of_sync") if ($component | is-not-empty) { let filtered = ($out_of_sync | where component == $component) if ($filtered | is-empty) { _print $"✅ Component '($component)' is already in sync or not found" return } $out_of_sync = $filtered } if ($out_of_sync | is-empty) { _print "✅ All taskservs are in sync with registry" return } _print $"Found ($out_of_sync | length) components with version mismatches:" for comp in $out_of_sync { _print $"\n🔧 ($comp.component) [Registry: ($comp.registry_version)]" # Find taskservs that need updating let outdated_taskservs = ($comp.taskserv_configs | where not matches_registry) for taskserv in $outdated_taskservs { if $dry_run { _print $"🔍 Would update ($taskserv.id): ($taskserv.version) -> ($comp.registry_version)" } else { _print $"🔄 Updating ($taskserv.id): ($taskserv.version) -> ($comp.registry_version)" update-nickel-version $taskserv.file $comp.registry_version } } } if $dry_run { _print "\n🔍 Dry run completed - no changes made" } else { _print "\n✅ Sync completed" } }