prvng_core/nulib/provisioning workspace
2025-10-07 10:32:04 +01:00

602 lines
21 KiB
Plaintext
Executable File

#!/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<string> = [] # Taskservs to load during init
--providers: list<string> = [] # Providers to load during init
--clusters: list<string> = [] # 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 <name>"
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 <type> [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 <name>"
print " Generate provider config from template"
print ""
print " config edit <type> [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 <name> 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<string>
--providers: list<string>
--clusters: list<string>
--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 <path>' 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 <workspace-path>"
print " 2. Or create a new workspace: provisioning ws init <name>"
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 <name>"
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/<name> # Navigate to infrastructure"
print $" provisioning ws init . --infra <name> # 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
}