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

481 lines
17 KiB
Text
Raw Normal View History

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