#!/usr/bin/env nu # Info: Script to run Provisioning Workspace Management # Author: JesusPerezLorenzo # Release: 1.0.0 # Date: 29-09-2025 use std log use lib_provisioning * use env.nu * # - > Help on Workspace export def "main help" [ --src: string = "" --notitles # not titles --out: string # Print Output format: json, yaml, text (default) ]: nothing -> nothing { if $notitles == null or not $notitles { show_titles } ^$"($env.PROVISIONING_NAME)" -mod workspace --help if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false } print (provisioning_options $src) if not $env.PROVISIONING_DEBUG { end_run "" } } # > Workspace Management def main [ ...args: string # Other options, use help to get info -v # Show version -i # Show Info --version (-V) # Show version with title --info (-I) # Show Info with title --about (-a) # Show About --infra: string # Infrastructure name --template: string = "minimal" # Template type: minimal, full, example --workspace-type: string = "development" # Workspace type --user-name: string = "" # User name for workspace --taskservs: list = [] # Taskservs to load during init --providers: list = [] # Providers to load during init --clusters: list = [] # Clusters to load during init --dep-option: string = "" # Dependency option: home-package, git-package, publish-repo, workspace-home --dep-url: string = "" # Dependency URL for git-package or publish-repo --overwrite # Overwrite existing workspace --check (-c) # Only check mode, no actual changes --yes (-y) # Confirm task --debug (-x) # Use Debug mode --xm # Debug with PROVISIONING_METADATA --xld # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug --metadata # Error with metadata (-xm) --notitles # Do not show banner titles --helpinfo (-h) # For more details use options "help" (no dashes) --out: string # Print Output format: json, yaml, text (default) ]: nothing -> nothing { if ($out | is-not-empty) { $env.PROVISIONING_OUT = $out $env.PROVISIONING_NO_TERMINAL = true } provisioning_init $helpinfo "workspace" $args if $version or $v { ^$env.PROVISIONING_NAME -v ; exit } if $info or $i { ^$env.PROVISIONING_NAME -i ; exit } if $about { _print (get_about_info) exit } if $debug { $env.PROVISIONING_DEBUG = true } if $metadata { $env.PROVISIONING_METADATA = true } let task = if ($args | length) > 0 { ($args | get 0) } else { "" } let ops = $"($env.PROVISIONING_ARGS? | default "") " | str replace $" ($task) " "" | str trim # Extract workspace path (first non-flag argument) let workspace_path = if ($ops | is-not-empty) and not ($ops | str starts-with "-") { let parts = ($ops | split row " ") let first_part = ($parts | get 0) # Stop at first flag if ($first_part | str starts-with "-") { "." } else { $first_part } } else { "." } $env.PROVISIONING_MODULE = "workspace" match $task { "h" | "help" => { # Redirect to main categorized help system exec $"($env.PROVISIONING_NAME)" help workspace --notitles }, "init" => { if $overwrite and $check { workspace_init $workspace_path --infra $infra --template $template --workspace-type $workspace_type --user-name $user_name --taskservs $taskservs --providers $providers --clusters $clusters --dep-option $dep_option --dep-url $dep_url --overwrite --check } else if $overwrite { workspace_init $workspace_path --infra $infra --template $template --workspace-type $workspace_type --user-name $user_name --taskservs $taskservs --providers $providers --clusters $clusters --dep-option $dep_option --dep-url $dep_url --overwrite } else if $check { workspace_init $workspace_path --infra $infra --template $template --workspace-type $workspace_type --user-name $user_name --taskservs $taskservs --providers $providers --clusters $clusters --dep-option $dep_option --dep-url $dep_url --check } else { workspace_init $workspace_path --infra $infra --template $template --workspace-type $workspace_type --user-name $user_name --taskservs $taskservs --providers $providers --clusters $clusters --dep-option $dep_option --dep-url $dep_url } }, "create" => { print $"📁 Creating workspace: ($workspace_path)" if $check { workspace_create $workspace_path --infra $infra --template $template --check } else { workspace_create $workspace_path --infra $infra --template $template } }, "migrate" => { print $"🔄 Migrating workspace: ($workspace_path)" if $check { workspace_migrate $workspace_path --check } else { workspace_migrate $workspace_path } }, "validate" => { let result = workspace_validate $workspace_path if $result.valid { print $"✅ Workspace is valid: ($workspace_path)" } else { print $"❌ Workspace validation failed: ($workspace_path)" print $"Missing directories: ($result.missing_directories)" print $"Missing files: ($result.missing_files)" print $"Recommendations: ($result.recommendations)" } if ($out | is-not-empty) { if $out == "json" { print ($result | to json) } else if $out == "yaml" { print ($result | to yaml) } else { print ($result | table) } } }, "info" => { let info = workspace_info $workspace_path print $"📊 Workspace Information:" print $" Path: ($info.workspace)" print $" Taskservs: ($info.taskservs_count) - ($info.taskservs | str join ', ')" print $" Providers: ($info.providers_count) - ($info.providers | str join ', ')" print $" Clusters: ($info.clusters_count) - ($info.clusters | str join ', ')" if ($out | is-not-empty) { if $out == "json" { print ($info | to json) } else if $out == "yaml" { print ($info | to yaml) } else { print ($info | table) } } }, "status" => { let status = workspace_status $workspace_path print $"📈 Workspace Status:" print $" Path: ($status.workspace)" print $" Has new structure: ($status.has_new_structure)" print $" Is migrated: ($status.is_migrated)" print $" Backup files: ($status.backup_files)" if ($out | is-not-empty) { if $out == "json" { print ($status | to json) } else if $out == "yaml" { print ($status | to yaml) } else { print ($status | table) } } }, "list" => { workspace_list --out $out }, "rollback" => { if not $yes { print "⚠️ This will rollback the migration. Use --yes to confirm." exit 1 } workspace_rollback $workspace_path }, "dry-run" => { workspace_dry_run $workspace_path }, "config" => { # Config subcommands let config_cmd = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { "help" } let config_ops = $ops | str replace $config_cmd "" | str trim match $config_cmd { "show" => { let format_flag = if ($out | is-not-empty) { $out } else { "yaml" } let ws_name = if ($infra | is-not-empty) { $infra } else { "" } workspace-config-show $ws_name --format $format_flag }, "validate" => { let ws_name = if ($infra | is-not-empty) { $infra } else { "" } workspace-config-validate $ws_name }, "generate" => { let parts = ($config_ops | split row " ") if ($parts | length) < 2 { print "Usage: provisioning workspace config generate provider " exit 1 } let gen_type = ($parts | get 0) let gen_name = ($parts | get 1) if $gen_type == "provider" { let ws_name = if ($infra | is-not-empty) { $infra } else { "" } workspace-config-generate-provider $gen_name $ws_name } else { print $"Unknown generate type: ($gen_type)" exit 1 } }, "edit" => { let parts = ($config_ops | split row " ") if ($parts | length) < 1 { print "Usage: provisioning workspace config edit [name]" print "Types: main, provider, platform, kms" exit 1 } let edit_type = ($parts | get 0) let edit_name = if ($parts | length) > 1 { ($parts | get 1) } else { "" } let ws_name = if ($infra | is-not-empty) { $infra } else { "" } workspace-config-edit $edit_type $edit_name $ws_name }, "hierarchy" => { let ws_name = if ($infra | is-not-empty) { $infra } else { "" } workspace-config-hierarchy $ws_name }, "list" => { let ws_name = if ($infra | is-not-empty) { $infra } else { "" } let type_flag = if ($config_ops | is-not-empty) { $config_ops } else { "all" } workspace-config-list $ws_name --type $type_flag }, "help" => { print "Workspace Configuration Commands:" print "" print " config show [name] [--format yaml|json|toml]" print " Show workspace configuration" print "" print " config validate [name]" print " Validate all workspace configs" print "" print " config generate provider " print " Generate provider config from template" print "" print " config edit [name]" print " Edit configuration file" print " Types: main, provider, platform, kms" print "" print " config hierarchy [name]" print " Show configuration loading hierarchy" print "" print " config list [name] [--type all|provider|platform|kms]" print " List configuration files" print "" print "Use --infra to specify workspace, or uses active workspace" }, _ => { print $"Unknown config command: ($config_cmd)" print "Use 'provisioning workspace config help' for available commands" exit 1 } } }, _ => { print $"❌ Unknown task: ($task)" print "Use 'provisioning workspace help' for available commands" exit 1 } } } # Initialize workspace def workspace_init [ path: string --infra: string --template: string --workspace-type: string --user-name: string --taskservs: list --providers: list --clusters: list --dep-option: string --dep-url: string --overwrite --check ]: nothing -> nothing { # Validate workspace name - no hyphens allowed (causes KCL module resolution issues) let workspace_name = ($path | path basename) if ($workspace_name | str contains "-") { print $"❌ Error: Workspace name cannot contain hyphens: '($workspace_name)'" print "" print " Hyphens in workspace names cause KCL module resolution issues." print $" Use underscores instead: '($workspace_name | str replace -a '-' '_')'" print "" exit 1 } # Validate infra name if provided - no hyphens allowed if ($infra | is-not-empty) and ($infra | str contains "-") { print $"❌ Error: Infrastructure name cannot contain hyphens: '($infra)'" print "" print " Hyphens in infrastructure names cause KCL module resolution issues." print $" Use underscores instead: '($infra | str replace -a '-' '_')'" print "" exit 1 } let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let init_script = ($provisioning_root | path join "tools" "workspace-init.nu") if not ($init_script | path exists) { print $"❌ Workspace init script not found: ($init_script)" exit 1 } let infra_flag = if ($infra | is-not-empty) { ["--infra-name" $infra] } else { [] } let template_flag = if ($template | is-not-empty) { ["--template" $template] } else { [] } let workspace_type_flag = if ($workspace_type | is-not-empty) { ["--workspace-type" $workspace_type] } else { [] } let user_flag = if ($user_name | is-not-empty) { ["--user-name" $user_name] } else { [] } let dep_option_flag = if ($dep_option | is-not-empty) { ["--dep-option" $dep_option] } else { [] } let dep_url_flag = if ($dep_url | is-not-empty) { ["--dep-url" $dep_url] } else { [] } let overwrite_flag = if $overwrite { ["--overwrite"] } else { [] } let check_flag = if $check { ["--check"] } else { [] } let all_flags = ($infra_flag | append $template_flag | append $workspace_type_flag | append $user_flag | append $dep_option_flag | append $dep_url_flag | append $overwrite_flag | append $check_flag) if ($taskservs | is-empty) and ($providers | is-empty) and ($clusters | is-empty) { ^nu $init_script $path ...$all_flags } else { let taskservs_flag = if not ($taskservs | is-empty) { ["--taskservs" $taskservs] } else { [] } let providers_flag = if not ($providers | is-empty) { ["--providers" $providers] } else { [] } let clusters_flag = if not ($clusters | is-empty) { ["--clusters" $clusters] } else { [] } ^nu $init_script init $path ...$all_flags ...$taskservs_flag ...$providers_flag ...$clusters_flag } } # Create workspace structure def workspace_create [ path: string --infra: string --template: string --check ]: nothing -> nothing { if $check { workspace_init $path --infra $infra --template $template --check } else { workspace_init $path --infra $infra --template $template } } # Migrate workspace def workspace_migrate [ path: string --check ]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "tools" "workspace-migrate.nu") if not ($migrate_script | path exists) { print $"❌ Workspace migrate script not found: ($migrate_script)" exit 1 } if $check { ^nu $migrate_script dry-run $path } else { ^nu $migrate_script $path } } # Validate workspace def workspace_validate [path: string]: nothing -> record { let workspace_abs = ($path | path expand) let required_dirs = [".taskservs", ".providers", ".clusters", ".manifest"] let required_files = ["kcl.mod"] let missing_dirs = $required_dirs | where {|dir| not (($workspace_abs | path join $dir) | path exists) } let missing_files = $required_files | where {|file| not (($workspace_abs | path join $file) | path exists) } let is_valid = ($missing_dirs | is-empty) and ($missing_files | is-empty) { valid: $is_valid workspace: $workspace_abs missing_directories: $missing_dirs missing_files: $missing_files recommendations: (if not $is_valid { ["Run 'provisioning workspace init ' to create or fix workspace structure"] } else { [] }) } } # Show workspace info def workspace_info [path: string]: nothing -> record { let workspace_abs = ($path | path expand) if not ($workspace_abs | path exists) { print $"❌ Workspace does not exist: ($workspace_abs)" exit 1 } let taskservs_manifest_path = ($workspace_abs | path join ".manifest" | path join "taskservs.yaml") let taskservs_manifest = if ($taskservs_manifest_path | path exists) { open $taskservs_manifest_path } else { { loaded_taskservs: [] } } let providers_manifest_path = ($workspace_abs | path join ".manifest" | path join "providers.yaml") let providers_manifest = if ($providers_manifest_path | path exists) { open $providers_manifest_path } else { { loaded_providers: [] } } let clusters_manifest_path = ($workspace_abs | path join ".manifest" | path join "clusters.yaml") let clusters_manifest = if ($clusters_manifest_path | path exists) { open $clusters_manifest_path } else { { loaded_clusters: [] } } { workspace: $workspace_abs taskservs_count: ($taskservs_manifest.loaded_taskservs? | default [] | length) providers_count: ($providers_manifest.loaded_providers? | default [] | length) clusters_count: ($clusters_manifest.loaded_clusters? | default [] | length) taskservs: ($taskservs_manifest.loaded_taskservs? | default [] | get -o name | default []) providers: ($providers_manifest.loaded_providers? | default [] | get -o name | default []) clusters: ($clusters_manifest.loaded_clusters? | default [] | get -o name | default []) } } # Show workspace migration status def workspace_status [path: string]: nothing -> record { let workspace_abs = ($path | path expand) let has_new_structure = [".taskservs", ".providers", ".clusters", ".manifest"] | all {|dir| ($workspace_abs | path join $dir) | path exists } let migration_info_path = ($workspace_abs | path join ".migration-info.yaml") let migration_info = if ($migration_info_path | path exists) { open $migration_info_path } else { null } let backup_pattern = ($workspace_abs | path join "**/*.bak") let backup_result = (do { glob $backup_pattern | length } | complete) let backup_files = if $backup_result.exit_code == 0 { $backup_result.stdout } else { 0 } { workspace: $workspace_abs has_new_structure: $has_new_structure migration_info: $migration_info backup_files: $backup_files is_migrated: ($has_new_structure and ($migration_info != null)) } } # List available workspaces def workspace_list [--out: string]: nothing -> nothing { let workspace_root = $env.PWD let infra_path = ($workspace_root | path join "infra") # Check if we're in a workspace directory if not ($infra_path | path exists) { print $"📁 Current directory: ($workspace_root)" print "❌ Not in a workspace directory (no 'infra/' subdirectory found)" print "" print "To use this command:" print " 1. Navigate to a workspace directory: cd " print " 2. Or create a new workspace: provisioning ws init " print "" print "Example:" print " provisioning ws init my-workspace" print " cd my-workspace" print " provisioning ws list" return } # Get list of infrastructure directories let infra_dirs = (ls $infra_path | where type == dir) if ($infra_dirs | is-empty) { print $"📁 Workspace: ($workspace_root)" print "⚠️ No infrastructure configurations found in infra/" print "" print "Create an infrastructure:" print " provisioning ws init . --infra " return } # Build infrastructure info let infra_info = ($infra_dirs | each {|dir| let infra_name = ($dir.name | path basename) let infra_full_path = $dir.name let has_servers = ($infra_full_path | path join "servers.k") | path exists let has_kcl_mod = ($infra_full_path | path join "kcl.mod") | path exists # Count loaded modules let taskservs_manifest = ($infra_full_path | path join ".manifest" "taskservs.yaml") let providers_manifest = ($infra_full_path | path join ".manifest" "providers.yaml") let taskservs_count = if ($taskservs_manifest | path exists) { let manifest = (open $taskservs_manifest) ($manifest | get -o loaded_taskservs | default [] | length) } else { 0 } let providers_count = if ($providers_manifest | path exists) { let manifest = (open $providers_manifest) ($manifest | get -o loaded_providers | default [] | length) } else { 0 } { name: $infra_name path: $infra_full_path servers_config: $has_servers kcl_mod: $has_kcl_mod taskservs: $taskservs_count providers: $providers_count } }) # Output based on format if ($out | is-not-empty) { if $out == "json" { print ($infra_info | to json) } else if $out == "yaml" { print ($infra_info | to yaml) } else { print ($infra_info | table) } } else { # Pretty output print $"📁 Workspace: ($workspace_root)" print $"📂 Infrastructure configurations: ($infra_info | length)" print "" $infra_info | each {|infra| print $" (_ansi green_bold)($infra.name)(_ansi reset)" print $" Path: infra/($infra.name)" print $" Servers config: ($infra.servers_config)" print $" KCL module: ($infra.kcl_mod)" print $" Taskservs loaded: ($infra.taskservs)" print $" Providers loaded: ($infra.providers)" print "" } print "Usage:" print $" cd infra/ # Navigate to infrastructure" print $" provisioning ws init . --infra # Add new infrastructure" } } # Rollback workspace migration def workspace_rollback [path: string]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "tools" "workspace-migrate.nu") if not ($migrate_script | path exists) { print $"❌ Workspace migrate script not found: ($migrate_script)" exit 1 } ^nu $migrate_script rollback $path } # Dry run migration def workspace_dry_run [path: string]: nothing -> nothing { let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning" let migrate_script = ($provisioning_root | path join "tools" "workspace-migrate.nu") if not ($migrate_script | path exists) { print $"❌ Workspace migrate script not found: ($migrate_script)" exit 1 } ^nu $migrate_script dry-run $path }