provisioning-core/nulib/domain/workspace/commands.nu

480 lines
17 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Workspace Management CLI Commands
# Commands for switching between workspaces and managing workspace registry
# Selective imports (ADR-025 Phase 3 Layer 2).
# Pre-existing name collision: get-workspace-path and list-workspaces are
# exported by BOTH user/config.nu and workspace/notation.nu. Original star
# imports resolved via last-wins (notation.nu line was after user/config.nu).
# Keep notation.nu as the owner for the 2 collision symbols.
use platform/user/config.nu [
get-active-workspace get-active-workspace-details get-user-preference
load-user-config register-workspace remove-workspace set-active-workspace
set-user-preference set-workspace-default-infra validate-workspace-exists
]
use primitives/io/hints.nu [show-next-step]
use platform/activation.nu [activate-workspace-platform]
use platform/workspace/notation.nu [
get-workspace-path list-workspaces parse-workspace-infra-notation
]
use platform/clients/fallback.nu [tool-list]
use primitives/io/fmt_output.nu [fmt-output]
# Activate a workspace (set as current)
export def "workspace activate" [
spec: string # Name of workspace or workspace:infra notation
--quiet (-q) # Suppress output messages
] {
let parsed = (parse-workspace-infra-notation $spec)
let workspace_name = $parsed.workspace
if not (validate-workspace-exists $workspace_name) {
print $"(ansi red)✗(ansi reset) Workspace '($workspace_name)' not found in registry"
print $"(ansi yellow)💡(ansi reset) Available workspaces:"
workspace list
print ""
print $"(ansi yellow)💡(ansi reset) Register it first with: provisioning workspace register ($workspace_name) <path>"
return
}
let workspace_path = (get-workspace-path $workspace_name)
if not ($workspace_path | path exists) {
print $"(ansi red)✗(ansi reset) Workspace directory not found: ($workspace_path)"
print $"(ansi yellow)💡(ansi reset) The workspace may have been moved or deleted"
return
}
let config_path = ($workspace_path | path join "config")
if not ($config_path | path exists) {
print $"(ansi red)✗(ansi reset) Workspace is not migrated to new config system"
print $"(ansi yellow)💡(ansi reset) Missing: ($config_path)"
print $"(ansi yellow)💡(ansi reset) Run migration: provisioning workspace migrate ($workspace_name)"
return
}
let provisioning_nickel = ($config_path | path join "provisioning.ncl")
let provisioning_yaml = ($config_path | path join "provisioning.yaml")
if not (($provisioning_nickel | path exists) or ($provisioning_yaml | path exists)) {
print $"(ansi red)✗(ansi reset) Missing workspace configuration"
print $"(ansi yellow)💡(ansi reset) Missing: ($provisioning_nickel) or ($provisioning_yaml)"
print $"(ansi yellow)💡(ansi reset) Run migration: provisioning workspace migrate ($workspace_name)"
return
}
set-active-workspace $workspace_name $workspace_path
if ($parsed.infra | is-not-empty) {
let infra_path = ([$workspace_path "infra" $parsed.infra] | path join)
let settings_file = ([$infra_path "settings.ncl"] | path join)
if not ($settings_file | path exists) {
print $"(ansi red)✗(ansi reset) Infrastructure '($parsed.infra)' not found in workspace '($workspace_name)'"
print $"(ansi yellow)💡(ansi reset) Missing: ($settings_file)"
return
}
set-workspace-default-infra $workspace_name $parsed.infra
if not $quiet {
print $"(ansi green)✓(ansi reset) Default infrastructure set to '($parsed.infra)'"
}
}
let platform_activated = (activate-workspace-platform $workspace_name --auto-start --wait --skip-optional)
if not $platform_activated {
print $"(ansi yellow)⚠️(ansi reset) Platform activation warning: services not fully available"
print $"(ansi yellow)💡(ansi reset) You can still use the workspace, but some services may not be available"
}
if not $quiet {
show-next-step "workspace_activate" {name: $workspace_name}
}
}
# Switch to another workspace (alias for activate)
export def "workspace switch" [
workspace_name: string # Name of workspace to switch to
--quiet (-q) # Suppress output messages
] {
workspace activate $workspace_name --quiet=$quiet
}
# List all known workspaces
export def "workspace list" [
--fmt (-f): string = "text" # Output format: text, json, yaml, toml, md
--clip (-c) # Copy output to clipboard
--notitles # Suppress section title and spacing (text mode only)
] {
# Tier-1 daemon / Tier-2 provisioning-tool / Tier-3 Nushell legacy
let workspaces = (tool-list "workspace_list" {} { list-workspaces })
if ($workspaces | is-empty) {
if not $notitles {
print $"(ansi yellow)No workspaces registered(ansi reset)"
print ""
print $"(ansi cyan)💡(ansi reset) Register a workspace with:"
print $" provisioning workspace register <name> <path>"
}
return
}
if $fmt != "text" {
let rendered = (fmt-output $workspaces $fmt $clip)
print $rendered
return
}
# text (human) rendering
if not $notitles {
print ""
print $"(ansi green_bold)Registered Workspaces:(ansi reset)"
print ""
}
let active = (get-active-workspace)
let lines = ($workspaces | each { |ws|
let is_active = ($ws.name == $active)
let active_marker = if $is_active { $"(ansi green)●(ansi reset)" } else { " " }
let name_display = if $is_active {
$"(ansi green_bold)($ws.name)(ansi reset)"
} else {
$ws.name
}
$" ($active_marker) ($name_display)\n Path: ($ws.path?|default "")\n Last used: ($ws.last_used?|default "")"
})
let output = ($lines | str join "\n")
if $clip {
# strip ANSI for clipboard
let plain = ($lines | each {|l| $l | ansi strip} | str join "\n")
fmt-output $plain "text" true | ignore
}
print $output
}
# Show currently active workspace
export def "workspace active" [] {
let active_workspace = (get-active-workspace)
if ($active_workspace == null) {
print $"(ansi yellow)No active workspace(ansi reset)"
print ""
print $"(ansi cyan)💡(ansi reset) Activate a workspace with:"
print $" provisioning workspace activate <name>"
return
}
let workspace_details = (get-active-workspace-details)
print ""
print $"(ansi green_bold)Active Workspace:(ansi reset)"
print $" Name: (ansi green)($workspace_details.name)(ansi reset)"
print $" Path: ($workspace_details.path)"
print $" Last used: ($workspace_details.last_used)"
print ""
}
# Register a workspace in the central registry
export def "workspace register" [
workspace_name: string # Name of workspace
workspace_path: string # Path to workspace directory
--activate (-a) # Activate after registering
] {
let expanded_path = ($workspace_path | path expand)
if not ($expanded_path | path exists) {
print $"(ansi red)✗(ansi reset) Directory not found: ($expanded_path)"
return
}
if ($expanded_path | path type) != "dir" {
print $"(ansi red)✗(ansi reset) Not a directory: ($expanded_path)"
return
}
register-workspace $workspace_name $expanded_path
if $activate {
print ""
workspace activate $workspace_name
}
}
# Remove workspace from registry (does not delete files)
export def "workspace remove" [
workspace_name: string # Name of workspace to remove
--force (-f) # Skip confirmation prompt
] {
if not (validate-workspace-exists $workspace_name) {
print $"(ansi red)✗(ansi reset) Workspace '($workspace_name)' not found in registry"
return
}
if not $force {
print $"(ansi yellow)⚠(ansi reset) This will remove '($workspace_name)' from the registry"
print $"(ansi cyan)(ansi reset) The workspace files will NOT be deleted"
print ""
let response = (input "Continue? (y/N): ")
if $response != "y" and $response != "Y" {
print $"(ansi yellow)Cancelled(ansi reset)"
return
}
}
remove-workspace $workspace_name
}
# Show workspace preferences
export def "workspace preferences" [
--format (-f): string = "table" # Output format: table, json, yaml
] {
let config = (load-user-config)
match $format {
"json" => { $config.preferences | to json }
"yaml" => { $config.preferences | to yaml }
_ => {
print ""
print $"(ansi green_bold)User Preferences:(ansi reset)"
print ""
$config.preferences | transpose key value | each { |pref|
print $" ($pref.key): (ansi cyan)($pref.value)(ansi reset)"
}
print ""
}
}
}
# Set workspace preference
export def "workspace set-preference" [
key: string # Preference key
value: string # Preference value
] {
set-user-preference $key $value
}
# Get workspace preference
export def "workspace get-preference" [
key: string # Preference key
] {
let value = (get-user-preference $key)
if ($value == null) {
print $"(ansi yellow)Preference '($key)' not found(ansi reset)"
} else {
print $value
}
}
# Show workspace version information
export def "workspace version" [
workspace_name?: string # Workspace name (default: active workspace)
--format (-f): string = "table" # Output format: table, json, yaml
] {
use domain/workspace/version.nu *
let ws_name = if ($workspace_name | is-not-empty) {
$workspace_name
} else {
let active = (get-active-workspace)
if ($active == null or ($active | is-empty)) {
print $"(ansi red)No active workspace(ansi reset)"
print $"(ansi yellow)Specify workspace name or activate a workspace first(ansi reset)"
return
}
$active
}
let ws_path = (get-workspace-path $ws_name)
if ($ws_path == null or not ($ws_path | path exists)) {
print $"(ansi red)Workspace not found: ($ws_name)(ansi reset)"
return
}
let summary = (get-version-summary $ws_path)
match $format {
"json" => { $summary | to json }
"yaml" => { $summary | to yaml }
_ => {
print ""
print $"(ansi green_bold)Workspace Version Information(ansi reset)"
print ""
print $"(ansi cyan)System:(ansi reset)"
print $" Version: ($summary.system.version)"
print ""
print $"(ansi cyan)Workspace:(ansi reset)"
print $" Name: ($summary.workspace.name)"
print $" Path: ($summary.workspace.path)"
print $" Version: ($summary.workspace.version)"
print $" Schema Version: ($summary.workspace.schema_version)"
print $" Format Version: ($summary.workspace.format_version)"
print $" Created: ($summary.workspace.created)"
print $" Last Updated: ($summary.workspace.last_updated)"
print ""
print $"(ansi cyan)Compatibility:(ansi reset)"
print $" Compatible: ($summary.compatibility.compatible)"
print $" Reason: ($summary.compatibility.reason)"
print $" Message: ($summary.compatibility.message)"
if ($summary.compatibility.requires_migration? | default false) {
print $" Requires Migration: (ansi yellow)Yes(ansi reset)"
}
print ""
print $"(ansi cyan)Migrations:(ansi reset)"
print $" Total: ($summary.migrations.count)"
if ($summary.migrations.last_migration != null) {
let last = $summary.migrations.last_migration
print $" Last Migration:"
print $" From: ($last.from_version) → To: ($last.to_version)"
print $" Type: ($last.migration_type)"
print $" Time: ($last.timestamp)"
print $" Success: ($last.success)"
}
print ""
}
}
}
# Migrate workspace to newer version
export def "workspace migrate" [
workspace_name?: string # Workspace name (default: active workspace)
--skip-backup (-s)
--force (-f)
--target-version: string
] {
use domain/workspace/migration.nu *
let ws_name = if ($workspace_name | is-not-empty) {
$workspace_name
} else {
let active = (get-active-workspace)
if ($active == null or ($active | is-empty)) {
print $"(ansi red)No active workspace(ansi reset)"
print $"(ansi yellow)Specify workspace name or activate a workspace first(ansi reset)"
return
}
$active
}
let result = (migrate-workspace $ws_name --skip-backup=$skip_backup --force=$force --target-version=($target_version | default ""))
if not $result.success {
if ($result.cancelled? | default false) { return }
print $"(ansi red)Migration failed(ansi reset)"
if ($result.error | is-not-empty) { print $"Error: ($result.error)" }
exit 1
}
if ($result.no_migration_needed? | default false) {
print $"(ansi green)✓(ansi reset) ($result.message)"
return
}
print $"(ansi green)Migration completed successfully(ansi reset)"
}
# Check workspace compatibility
export def "workspace check-compatibility" [
workspace_name?: string # Workspace name (default: active workspace)
] {
use domain/workspace/version.nu *
let ws_name = if ($workspace_name | is-not-empty) {
$workspace_name
} else {
let active = (get-active-workspace)
if ($active == null or ($active | is-empty)) {
print $"(ansi red)No active workspace(ansi reset)"
print $"(ansi yellow)Specify workspace name or activate a workspace first(ansi reset)"
return
}
$active
}
let ws_path = (get-workspace-path $ws_name)
if ($ws_path == null or not ($ws_path | path exists)) {
print $"(ansi red)Workspace not found: ($ws_name)(ansi reset)"
return
}
let compatibility = (check-workspace-compatibility $ws_path)
print ""
print $"(ansi green_bold)Workspace Compatibility Check(ansi reset)"
print ""
print $"(ansi cyan)Workspace:(ansi reset) ($ws_name)"
print $"(ansi cyan)Path:(ansi reset) ($ws_path)"
print ""
let status_color = if $compatibility.compatible { "green" } else { "red" }
let status_icon = if $compatibility.compatible { "✓" } else { "✗" }
print $"(ansi ($status_color))($status_icon) ($compatibility.message)(ansi reset)"
print ""
if not $compatibility.compatible {
print $"(ansi yellow)Reason:(ansi reset) ($compatibility.reason)"
if ($compatibility.requires_migration? | default false) {
print ""
print $"(ansi cyan)To fix this, run:(ansi reset)"
print $" provisioning workspace migrate ($ws_name)"
} else if ($compatibility.requires_upgrade? | default false) {
print ""
print $"(ansi cyan)System or workspace needs upgrade(ansi reset)"
}
print ""
}
}
# List workspace backups
export def "workspace list-backups" [
workspace_name?: string # Workspace name (default: active workspace)
] {
use domain/workspace/migration.nu *
let ws_name = if ($workspace_name | is-not-empty) {
$workspace_name
} else {
let active = (get-active-workspace)
if ($active == null or ($active | is-empty)) {
print $"(ansi red)No active workspace(ansi reset)"
print $"(ansi yellow)Specify workspace name or activate a workspace first(ansi reset)"
return
}
$active
}
let backups = (list-workspace-backups $ws_name)
if ($backups | is-empty) {
print $"(ansi yellow)No backups found for workspace '($ws_name)'(ansi reset)"
return
}
print ""
print $"(ansi green_bold)Workspace Backups for ($ws_name)(ansi reset)"
print ""
$backups | select name created reason size | table
}
# Restore workspace from backup
export def "workspace restore-backup" [
backup_path: string # Path to backup directory
--force (-f)
] {
use domain/workspace/migration.nu *
let result = (restore-workspace-from-backup $backup_path --force=$force)
if not $result.success {
if ($result.cancelled? | default false) { return }
print $"(ansi red)Restore failed(ansi reset)"
if ($result.error | is-not-empty) { print $"Error: ($result.error)" }
exit 1
}
}