Update core components including CLI, Nushell libraries, plugins system, and utility scripts for the provisioning system. CLI Updates: - Command implementations - CLI utilities and dispatching - Help system improvements - Command validation Library Updates: - Configuration management system - Infrastructure validation - Extension system improvements - Secrets management - Workspace operations - Cache management system Plugin System: - Interactive form plugin (inquire) - KCL integration plugin - Performance optimization plugins - Plugin registration system Utilities: - Build and distribution scripts - Installation procedures - Testing utilities - Development tools Documentation: - Library module documentation - Extension API guides - Plugin usage guides - Service management documentation All changes are backward compatible. No breaking changes.
414 lines
15 KiB
Plaintext
414 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: list/versions, check-updates, update, pin, unpin
|
|
...args # Additional arguments
|
|
--help(-h) # Show help
|
|
--notitles # Ignored flag
|
|
]: nothing -> any {
|
|
if $help {
|
|
show_taskserv_help
|
|
return
|
|
}
|
|
|
|
# Show help if no command provided
|
|
if ($command | is-empty) {
|
|
show_taskserv_help
|
|
return
|
|
}
|
|
|
|
match $command {
|
|
"versions" | "list" => {
|
|
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] {
|
|
print "📦 Available Taskservs:"
|
|
print ""
|
|
|
|
# Get taskservs paths from both extensions and workspace
|
|
# Try global extensions first, fall back to workspace extensions
|
|
let global_extensions_path = (($env.PROVISIONING_HOME? | default $env.HOME) | path join ".provisioning-extensions")
|
|
let workspace_taskservs_path = (config-get "paths.taskservs" | path expand)
|
|
|
|
# Determine which extensions path to use
|
|
let extensions_taskservs_path = if (($global_extensions_path | path join "taskservs" | path exists)) {
|
|
$global_extensions_path | path join "taskservs"
|
|
} else if (("/Users/Akasha/project-provisioning/provisioning/extensions/taskservs" | path exists)) {
|
|
"/Users/Akasha/project-provisioning/provisioning/extensions/taskservs"
|
|
} else {
|
|
$global_extensions_path | path join "taskservs"
|
|
}
|
|
|
|
# Discover all taskservs from both locations
|
|
mut all_taskservs = []
|
|
|
|
# Helper function to discover taskservs from a given directory
|
|
def discover_from_path [base_path: string] {
|
|
mut discovered = []
|
|
|
|
if not ($base_path | path exists) {
|
|
return $discovered
|
|
}
|
|
|
|
let items = (ls $base_path | where type == "dir")
|
|
|
|
for item in $items {
|
|
let group_name = ($item.name | path basename)
|
|
let group_path = $item.name
|
|
|
|
# First check if group itself has kcl/kcl.mod (group-level taskserv)
|
|
let group_kcl_path = ($group_path | path join "kcl")
|
|
let group_kcl_mod = ($group_kcl_path | path join "kcl.mod")
|
|
if ($group_kcl_mod | path exists) {
|
|
let metadata = {
|
|
name: $group_name
|
|
group: $group_name
|
|
}
|
|
$discovered = ($discovered | append $metadata)
|
|
}
|
|
|
|
# Then check for taskservs in group subdirectories
|
|
let subitems = (ls $group_path | where type == "dir")
|
|
|
|
for subitem in $subitems {
|
|
let app_name = ($subitem.name | path basename)
|
|
|
|
# Skip 'kcl' and 'images' directories
|
|
if (not ($app_name == "kcl") and not ($app_name == "images")) {
|
|
let kcl_path = ($subitem.name | path join "kcl")
|
|
let kcl_mod_path = ($kcl_path | path join "kcl.mod")
|
|
|
|
# Check if this application has a kcl/kcl.mod file
|
|
if ($kcl_mod_path | path exists) {
|
|
let metadata = {
|
|
name: $app_name
|
|
group: $group_name
|
|
}
|
|
$discovered = ($discovered | append $metadata)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $discovered
|
|
}
|
|
|
|
# Discover from both locations, with extensions taking precedence
|
|
$all_taskservs = ($all_taskservs | append (discover_from_path $extensions_taskservs_path))
|
|
$all_taskservs = ($all_taskservs | append (discover_from_path $workspace_taskservs_path))
|
|
|
|
# Remove duplicates (keep first occurrence, typically from extensions)
|
|
mut unique_keys = []
|
|
mut final_taskservs = []
|
|
for taskserv in $all_taskservs {
|
|
let key = $"($taskserv.group)/($taskserv.name)"
|
|
if ($key not-in $unique_keys) {
|
|
$unique_keys = ($unique_keys | append $key)
|
|
$final_taskservs = ($final_taskservs | append $taskserv)
|
|
}
|
|
}
|
|
$all_taskservs = $final_taskservs
|
|
|
|
if ($all_taskservs | is-empty) {
|
|
print "⚠️ No taskservs found"
|
|
return
|
|
}
|
|
|
|
# Filter by name if provided
|
|
let filtered = if ($name | is-not-empty) {
|
|
$all_taskservs | where ($it.name =~ $name) or ($it.group =~ $name)
|
|
} else {
|
|
$all_taskservs
|
|
}
|
|
|
|
if ($filtered | is-empty) {
|
|
print $"No taskserv found matching: ($name)"
|
|
return
|
|
}
|
|
|
|
# Group by group name and display
|
|
let grouped = ($filtered | group-by group | items { |group_name, items|
|
|
{ group: $group_name, apps: $items }
|
|
})
|
|
|
|
for group_info in ($grouped | sort-by group) {
|
|
print $" 📁 (_ansi cyan)($group_info.group)(_ansi reset)"
|
|
for app in ($group_info.apps | sort-by name) {
|
|
print $" • ($app.name)"
|
|
}
|
|
print ""
|
|
}
|
|
|
|
let count = ($filtered | length)
|
|
let groups = ($filtered | get group | uniq | length)
|
|
print $"Found ($count) taskservs"
|
|
print $" - ($groups) groups"
|
|
}
|
|
|
|
def show_taskserv_help [] {
|
|
print "Taskserv Management Commands:"
|
|
print ""
|
|
print " list [name] - List available taskservs"
|
|
print " versions [name] - List taskserv versions (alias: list)"
|
|
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 list # List all taskservs"
|
|
print " provisioning t list # List all (shortcut)"
|
|
print " provisioning taskserv list kubernetes # Show kubernetes info"
|
|
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 tag_name? | default null)
|
|
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 version? | default {} | get current? | default "")
|
|
source: ($result | get version? | default {} | get source? | default "")
|
|
check_latest: ($result | get version? | default {} | get 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 0? | default {} | get current_version? | default "not defined")
|
|
source_url: ($items | where has_version | get 0? | default {} | get source_url? | default "")
|
|
check_latest: ($items | where has_version | get 0? | default {} | get 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>"
|
|
}
|
|
} |