provisioning/core/nulib/main_provisioning/taskserv.nu

401 lines
15 KiB
Plaintext
Raw Normal View History

# 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 <name> <ver> - Update taskserv to specific version"
print " pin <name> - Pin taskserv version (disable updates)"
print " unpin <name> - 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 <name> <version>"
}
}