# Taskserv Management Commands # Purpose: Main interface for taskserv version management and operations # PAP Compliance: Config-driven, no hardcoding, graceful periods use lib_provisioning * # Main taskserv command dispatcher export def "main taskserv" [ command: string # Subcommand: versions, check-updates, update, pin, unpin ...args # Additional arguments --help(-h) # Show help ]: nothing -> any { if $help { show_taskserv_help return } match $command { "versions" => { if ($args | length) > 0 { show_taskserv_versions ($args | get 0) } else { show_taskserv_versions } } "check-updates" => { if ($args | length) > 0 { check_taskserv_updates ($args | get 0) } else { check_taskserv_updates } } "update" => { print "Feature not implemented yet. Available commands: versions" } "pin" => { print "Feature not implemented yet. Available commands: versions" } "unpin" => { print "Feature not implemented yet. Available commands: versions" } _ => { print $"Unknown taskserv command: ($command)" show_taskserv_help } } } def show_taskserv_versions [name?: string] { use ../lib_provisioning/config/accessor.nu get-taskservs-path print "📦 Taskserv Versions:" print "" let taskservs_path = (get-taskservs-path) if not ($taskservs_path | path exists) { print $"⚠️ Taskservs path not found: ($taskservs_path)" return } # Find all KCL files in taskservs let all_k_files = (glob $"($taskservs_path)/**/*.k") let all_taskservs = ($all_k_files | each { |kcl_file| # Skip __init__.k, schema files, and other utility files if ($kcl_file | str ends-with "__init__.k") or ($kcl_file | str contains "/wrks/") or ($kcl_file | str ends-with "taskservs/version.k") { null } else { let relative_path = ($kcl_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) >= 3 { # Like "containerd/default/kcl/containerd.k" $path_parts.0 } else if ($path_parts | length) == 2 { # Like "proxy/kcl/proxy.k" or special cases let filename = ($kcl_file | path basename | str replace ".k" "") if $path_parts.0 == "no" { $"($path_parts.0)::($filename)" } else { $path_parts.0 } } else { ($kcl_file | path basename | str replace ".k" "") } # Try to read from version.k file first, then fallback to schema extraction let version_file = ($kcl_file | path dirname | path join "version.k") let version = if ($version_file | path exists) { # Read version from version.k file using KCL let kcl_result = (^kcl $version_file | complete) if $kcl_result.exit_code == 0 and ($kcl_result.stdout | is-not-empty) { let result = ($kcl_result.stdout | from yaml) # Try new TaskservVersion schema format first (direct output structure) if ($result | get -o version.current | is-not-empty) { # New TaskservVersion schema format - direct structure ($result | get version.current) } else if ($result | get -o current | is-not-empty) { # Simple format for backward compatibility ($result | get current) } else { # Fallback to legacy naming convention let clean_id = ($id | split row "::" | last) let version_key = $"($clean_id)_version" let legacy_version_data = ($result | get -o $version_key | default {}) if ($legacy_version_data | get -o version.current | is-not-empty) { ($legacy_version_data | get version.current) } else if ($legacy_version_data | get -o current | is-not-empty) { ($legacy_version_data | get current) } else { "" } } } else { "" } } else { # Fallback to schema extraction for files without version.k use ../lib_provisioning/utils/version_taskserv.nu extract-kcl-version extract-kcl-version $kcl_file } { id: $id version: (if ($version | is-not-empty) { $version } else { "not defined" }) file: $kcl_file has_version: ($version | is-not-empty) } } } | where $it != null) # Remove duplicates and sort let unique_taskservs = ($all_taskservs | group-by id | items { |key, items| { id: $key version: ($items | where has_version | get -o 0.version | default "not defined") has_version: ($items | any { |item| $item.has_version }) } } | sort-by id) let filtered = if ($name | is-not-empty) { $unique_taskservs | where id =~ $name } else { $unique_taskservs } if ($filtered | is-empty) { print $"No taskserv found matching: ($name)" return } # Show with version status $filtered | each { |taskserv| let status = if $taskserv.has_version { "✅" } else { "⚠️" } print $" ($status) ($taskserv.id): ($taskserv.version)" } print "" let with_versions = ($filtered | where has_version | length) let without_versions = ($filtered | where (not has_version) | length) print $"Found ($filtered | length) taskservs" print $" - ($with_versions) with versions defined" print $" - ($without_versions) without versions" } def show_taskserv_help [] { print "Taskserv Management Commands:" print "" print " versions [name] - List taskserv versions" print " check-updates [name] - Check for available updates" print " update - Update taskserv to specific version" print " pin - Pin taskserv version (disable updates)" print " unpin - Unpin taskserv version (enable updates)" print "" print "Examples:" print " provisioning taskserv versions # List all versions" print " provisioning taskserv versions kubernetes # Show kubernetes version" print " provisioning taskserv check-updates # Check all for updates" print " provisioning taskserv update kubernetes 1.31.2 # Update kubernetes" print " provisioning taskserv pin kubernetes # Pin kubernetes version" } # Check for taskserv updates # Helper function to fetch latest version from GitHub API def fetch_latest_version [api_url: string, fallback: string, use_curl: bool]: nothing -> string { if $use_curl { let fetch_result = ^curl -s $api_url | complete if $fetch_result.exit_code == 0 { let response = $fetch_result.stdout | from json $response.tag_name | str replace "^v" "" } else { $fallback } } else { let response = (http get $api_url --headers [User-Agent "provisioning-version-checker"]) let response_version = ($response | get -o tag_name) if ($response_version | is-not-empty ) { $response_version | str replace "^v" "" } else { $fallback } } } def check_taskserv_updates [ taskserv_name?: string # Optional specific taskserv name ]: nothing -> nothing { use ../lib_provisioning/config/accessor.nu get-taskservs-path use ../lib_provisioning/config/accessor.nu get-config use ../lib_provisioning/config/loader.nu get-config-value print "🔄 Checking for taskserv updates..." print "" let taskservs_path = (get-taskservs-path) if not ($taskservs_path | path exists) { print $"⚠️ Taskservs path not found: ($taskservs_path)" return } # Get all taskservs (same logic as show_taskserv_versions) let all_k_files = (glob $"($taskservs_path)/**/*.k") let all_taskservs = ($all_k_files | each { |kcl_file| # Skip __init__.k, schema files, and other utility files if ($kcl_file | str ends-with "__init__.k") or ($kcl_file | str contains "/wrks/") or ($kcl_file | str ends-with "taskservs/version.k") { null } else { let relative_path = ($kcl_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) >= 3 { $path_parts.0 } else if ($path_parts | length) == 2 { let filename = ($kcl_file | path basename | str replace ".k" "") if $path_parts.0 == "no" { $"($path_parts.0)::($filename)" } else { $path_parts.0 } } else { ($kcl_file | path basename | str replace ".k" "") } # Read version data from version.k file let version_file = ($kcl_file | path dirname | path join "version.k") let version_info = if ($version_file | path exists) { let kcl_result = (^kcl $version_file | complete) if $kcl_result.exit_code == 0 and ($kcl_result.stdout | is-not-empty) { let result = ($kcl_result.stdout | from yaml) { current: ($result | get -o version.current | default "") source: ($result | get -o version.source | default "") check_latest: ($result | get -o version.check_latest | default false) has_version: true } } else { { current: "" source: "" check_latest: false has_version: false } } } else { { current: "" source: "" check_latest: false has_version: false } } { id: $id current_version: $version_info.current source_url: $version_info.source check_latest: $version_info.check_latest has_version: $version_info.has_version } } } | where $it != null) # Filter to unique taskservs and optionally filter by name let unique_taskservs = ($all_taskservs | group-by id | items { |key, items| { id: $key current_version: ($items | where has_version | get -o 0.current_version | default "not defined") source_url: ($items | where has_version | get -o 0.source_url | default "") check_latest: ($items | where has_version | get -o 0.check_latest | default false) has_version: ($items | any { |item| $item.has_version }) } } | sort-by id | if ($taskserv_name | is-not-empty) { where id == $taskserv_name } else { $in } ) if ($unique_taskservs | is-empty) { if ($taskserv_name | is-not-empty) { print $"❌ Taskserv '($taskserv_name)' not found" } else { print "❌ No taskservs found" } return } let config = get-config let use_curl = (get-config-value $config "http.use_curl" false) # Check updates for each taskserv let update_results = ($unique_taskservs | each { |taskserv| if not $taskserv.has_version { { id: $taskserv.id status: "no_version" current: "not defined" latest: "" update_available: false message: "No version defined" } } else if not $taskserv.check_latest { { id: $taskserv.id status: "pinned" current: $taskserv.current_version latest: "" update_available: false message: "Version pinned (check_latest = false)" } } else if ($taskserv.source_url | is-empty) { { id: $taskserv.id status: "no_source" current: $taskserv.current_version latest: "" update_available: false message: "No source URL for update checking" } } else { # Fetch latest version from GitHub releases API let api_url = $taskserv.source_url | str replace "github.com" "api.github.com/repos" | str replace "/releases" "/releases/latest" let latest_version = if ($taskserv.source_url | is-empty) { $taskserv.current_version } else { fetch_latest_version $api_url $taskserv.current_version $use_curl } let update_available = ($taskserv.current_version != $latest_version) let status = if $update_available { "update_available" } else { "up_to_date" } let message = if $update_available { $"Update available: ($taskserv.current_version) → ($latest_version)" } else { "Up to date" } { id: $taskserv.id status: $status current: $taskserv.current_version latest: $latest_version update_available: $update_available message: $message } } }) # Display results for result in $update_results { let icon = match $result.status { "update_available" => "🆙" "up_to_date" => "✅" "pinned" => "📌" "no_version" => "⚠️" "no_source" => "❓" _ => "❔" } print $" ($icon) ($result.id): ($result.message)" } print "" let total_count = ($update_results | length) let updates_available = ($update_results | where update_available | length) let pinned_count = ($update_results | where status == "pinned" | length) let no_version_count = ($update_results | where status == "no_version" | length) print $"📊 Summary: ($total_count) taskservs checked" print $" - ($updates_available) updates available" print $" - ($pinned_count) pinned" print $" - ($no_version_count) without version definitions" if $updates_available > 0 { print "" print "💡 To update a taskserv: provisioning taskserv update " } }