401 lines
15 KiB
Plaintext
401 lines
15 KiB
Plaintext
![]() |
# 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>"
|
||
|
}
|
||
|
}
|