Jesús Pérez 85ce530733
feat: update provisioning core CLI, libraries, and plugins
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.
2025-12-11 21:57:05 +00:00

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>"
}
}