provisioning-core/nulib/domain/version/manager.nu

243 lines
7.9 KiB
Text
Raw Permalink Normal View History

#!/usr/bin/env nu
# Main version management interface
# Completely configuration-driven, no hardcoded components
# Selective imports (ADR-025 Phase 3 Layer 2).
use domain/version/core.nu [check-version]
use domain/version/loader.nu [discover-configurations load-configuration-file]
use domain/version/formatter.nu [format-results]
use primitives/io/interface.nu [_print]
# Check versions for discovered components
export def check-versions [
--path: string = "" # Base path to search
--types: list = [] # Filter by types
--fetch-latest = false # Fetch latest versions
--respect-fixed = true # Respect fixed flag
--config-file: string = "" # Use specific config file
] {
# Load configurations
let configs = if ($config_file | is-not-empty) {
load-configuration-file $config_file
} else {
discover-configurations --base-path=$path --types=$types
}
# Check each configuration
$configs | each { |config|
check-version $config --fetch-latest=$fetch_latest --respect-fixed=$respect_fixed
}
}
# Display version status
export def show-versions [
--path: string = ""
--types: list = []
--fetch-latest = true
--group-by: string = "type"
--format: string = "table" # table, json, yaml
] {
let results = (check-versions --path=$path --types=$types --fetch-latest=$fetch_latest)
match $format {
"table" => {
format-results $results --group-by=$group_by
}
"json" => {
print ($results | to json -i 2)
}
"yaml" => {
print ($results | to yaml)
}
_ => {
format-results $results
}
}
}
# Check for available updates (does not modify configs)
export def check-available-updates [
--path: string = ""
--types: list = []
] {
let results = (check-versions --path=$path --types=$types --fetch-latest=true --respect-fixed=true)
let updates = ($results | where status == "update_available")
if ($updates | is-empty) {
_print "✅ All components are up to date"
return
}
_print "Updates available:"
_print ($updates | select id configured latest | rename id configured "latest available" | table)
# Show installation guidance for each update
for update in $updates {
let config = (discover-configurations --types=[$update.type]
| where id == $update.id
| first | default null)
if ($config | is-not-empty) {
show-installation-guidance $config $update.latest
}
}
_print $"\n💡 After installing, run 'tools apply-updates' to update configuration files"
}
# Apply updates to configuration files
export def apply-config-updates [
--path: string = ""
--types: list = []
--dry-run = false
--force = false # Update even if fixed
--auto-yes = false # Skip prompts and auto-confirm
] {
# Separate types from component ids (types are "provider", "generic"; ids are "upctl", "aws", etc.)
let all_configs = (discover-configurations --base-path=$path)
let known_types = ($all_configs | get type | uniq)
let filter_types = ($types | where { |t| $t in $known_types })
let filter_ids = ($types | where { |t| $t not-in $known_types })
# Check locally only (don't fetch latest to avoid GitHub API rate limits)
let results_local = if ($filter_types | is-empty) {
(check-versions --path=$path --fetch-latest=false --respect-fixed=(not $force))
} else {
(check-versions --path=$path --types=$filter_types --fetch-latest=false --respect-fixed=(not $force))
}
# Apply id filters if provided
let results = if ($filter_ids | length) > 0 {
$results_local | where id in $filter_ids
} else {
$results_local
}
# Find mismatches between installed and configured
let all_updates = ($results | where status in ["ahead_config", "behind_config"])
if ($all_updates | is-empty) {
_print "✅ All configurations match installed versions"
return
}
_print "Configuration updates available to match installed versions:"
_print ($all_updates | select id configured installed status | table)
if $dry_run {
_print "\n🔍 Dry run mode - no changes will be made"
return
}
if not $auto_yes {
let proceed = (input "Update configurations to match installed versions? (y/n): ")
if $proceed != "y" { return }
} else {
_print "Auto-confirming updates..."
}
# Update each component's configuration file
for update in $all_updates {
let config = (discover-configurations --types=[$update.type]
| where id == $update.id
| first | default null)
if ($config | is-not-empty) {
let source_file = $config.metadata.source_file
# Update to match installed version
update-configuration-file $source_file $update.id $update.installed
_print $"✅ Updated config ($update.id): ($update.configured) -> ($update.installed)"
}
}
}
# Show agnostic installation guidance
export def show-installation-guidance [
config: record
version: string
] {
_print $"\n📦 To install ($config.id) ($version):"
# Show documentation/site links from configuration
if ($config.metadata.site | is-not-empty) {
_print $" • Documentation: ($config.metadata.site)"
}
# Show source repository if available
if ($config.source.type? | default "" | str contains "github") {
let repo = ($config.source.repo? | default "")
if ($repo | is-not-empty) {
_print $" • Releases: https://github.com/($repo)/releases"
}
}
# Show generic installation command if available in metadata
if ($config.metadata.install_cmd? | default "" | is-not-empty) {
_print $" • Install: ($config.metadata.install_cmd)"
}
_print $"\n🔍 Configuration updated, manual installation required"
_print $"💡 Run 'tools check ($config.id)' after installation to verify"
}
# Update configuration file
export def update-configuration-file [
file_path: string
component_id: string
new_version: string
] {
if not ($file_path | path exists) { return }
let ext = ($file_path | path parse | get extension)
match $ext {
"yaml" | "yml" => {
let data = (open $file_path)
let updated = ($data | upsert $component_id ($data | get $component_id | upsert version $new_version))
$updated | save -f $file_path
}
"json" => {
let data = (open $file_path)
let updated = ($data | upsert $component_id ($data | get $component_id | upsert version $new_version))
$updated | to json -i 2 | save -f $file_path
}
"toml" => {
# TOML update would need proper TOML writer
print $"⚠️ TOML update not implemented for ($file_path)"
}
"k" => {
# Nickel update would need Nickel parser/writer
print $"⚠️ Nickel update not implemented for ($file_path)"
}
_ => {
print $"⚠️ Unknown file type: ($ext)"
}
}
}
# Pin/unpin component version
export def set-fixed [
component_id: string
fixed: bool
--path: string = ""
] {
let configs = (discover-configurations --base-path=$path)
let config = ($configs | where id == $component_id | first | default null)
if ($config | is-empty) {
print $"❌ Component '($component_id)' not found"
return
}
let source_file = $config.metadata.source_file
let data = (open $source_file)
let updated = ($data | upsert $component_id ($data | get $component_id | upsert fixed $fixed))
$updated | save -f $source_file
if $fixed {
print $"🔒 Pinned ($component_id) to version ($config.version)"
} else {
print $"🔓 Unpinned ($component_id)"
}
}