516 lines
15 KiB
Text
516 lines
15 KiB
Text
|
|
# Workspace Configuration Management Commands
|
|||
|
|
# Provides commands to view, edit, validate, and manage workspace configurations
|
|||
|
|
|
|||
|
|
use platform/user/config.nu [list-workspaces get-active-workspace get-workspace-path get-user-config-dir]
|
|||
|
|
use tools/nickel/process.nu [ncl-eval-soft]
|
|||
|
|
|
|||
|
|
# Get active workspace context or load by name
|
|||
|
|
def get-workspace-context [
|
|||
|
|
workspace_name?: string
|
|||
|
|
] {
|
|||
|
|
if ($workspace_name | is-empty) {
|
|||
|
|
let active = (get-active-workspace)
|
|||
|
|
if ($active | is-empty) {
|
|||
|
|
print "❌ No active workspace found. Use 'provisioning workspace activate <name>'"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
let path = (get-workspace-path $active)
|
|||
|
|
{
|
|||
|
|
name: $active
|
|||
|
|
path: $path
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
let contexts = (list-workspaces)
|
|||
|
|
|
|||
|
|
# First try to match by name
|
|||
|
|
let found = ($contexts | where name == $workspace_name | first)
|
|||
|
|
|
|||
|
|
if ($found | is-empty) {
|
|||
|
|
let input_as_path = if ($workspace_name | path exists) {
|
|||
|
|
$workspace_name | path expand
|
|||
|
|
} else {
|
|||
|
|
let rel_path = ($env.PWD | path join $workspace_name)
|
|||
|
|
if ($rel_path | path exists) {
|
|||
|
|
$rel_path | path expand
|
|||
|
|
} else {
|
|||
|
|
$workspace_name | path expand
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let path_exists = ($input_as_path | path exists)
|
|||
|
|
|
|||
|
|
if $path_exists {
|
|||
|
|
let found_by_path = ($contexts | where (path | path expand) == $input_as_path | first)
|
|||
|
|
|
|||
|
|
if ($found_by_path | is-not-empty) {
|
|||
|
|
return $found_by_path
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let config_file_nickel = ($input_as_path | path join "config" | path join "provisioning.ncl")
|
|||
|
|
let config_file_yaml = ($input_as_path | path join "config" | path join "provisioning.yaml")
|
|||
|
|
if (($config_file_nickel | path exists) or ($config_file_yaml | path exists)) {
|
|||
|
|
return {
|
|||
|
|
name: ($input_as_path | path basename)
|
|||
|
|
path: $input_as_path
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let found_by_exact_path = ($contexts | where path == $workspace_name | first)
|
|||
|
|
if ($found_by_exact_path | is-not-empty) {
|
|||
|
|
return $found_by_exact_path
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print $"❌ Workspace '($workspace_name)' not found"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
$found
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Show complete workspace configuration
|
|||
|
|
export def "workspace-config-show" [
|
|||
|
|
workspace_name?: string
|
|||
|
|
--format: string = "yaml" # yaml, json, toml, nickel
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
let config_dir = ($workspace.path | path join "config")
|
|||
|
|
let decl_file = ($config_dir | path join "provisioning.ncl")
|
|||
|
|
let yaml_file = ($config_dir | path join "provisioning.yaml")
|
|||
|
|
|
|||
|
|
let config_file = if ($decl_file | path exists) {
|
|||
|
|
let ncl_ok = (ncl-eval-soft $decl_file [] null | is-not-empty)
|
|||
|
|
if $ncl_ok {
|
|||
|
|
$decl_file
|
|||
|
|
} else if ($yaml_file | path exists) {
|
|||
|
|
$yaml_file
|
|||
|
|
} else {
|
|||
|
|
$decl_file
|
|||
|
|
}
|
|||
|
|
} else if ($yaml_file | path exists) {
|
|||
|
|
$yaml_file
|
|||
|
|
} else {
|
|||
|
|
null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($config_file | is-empty) {
|
|||
|
|
print "❌ No workspace configuration found (neither .ncl nor .yaml)"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let config = if ($config_file | str ends-with ".ncl") {
|
|||
|
|
let file_dir = ($config_file | path dirname)
|
|||
|
|
let file_name = ($config_file | path basename)
|
|||
|
|
let decl_mod_exists = (($file_dir | path join "nickel.mod") | path exists)
|
|||
|
|
|
|||
|
|
let parsed = if $decl_mod_exists {
|
|||
|
|
let res = (do { ^sh -c $"cd '($file_dir)' && nickel export ($file_name) --format json" } | complete)
|
|||
|
|
if ($res.stdout | is-not-empty) { $res.stdout | from json } else { null }
|
|||
|
|
} else {
|
|||
|
|
ncl-eval-soft $config_file [] null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($parsed | is-empty) {
|
|||
|
|
print "❌ Failed to load Nickel config: empty output"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
if (($parsed | columns) | any { |col| $col == "workspace_config" }) {
|
|||
|
|
$parsed.workspace_config
|
|||
|
|
} else {
|
|||
|
|
$parsed
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
open $config_file
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let config_type = if ($config_file | str ends-with ".ncl") { "Nickel" } else { "YAML" }
|
|||
|
|
|
|||
|
|
match $format {
|
|||
|
|
"yaml" => {
|
|||
|
|
print $"Configuration ($config_type): ($config_file)"
|
|||
|
|
print ""
|
|||
|
|
($config | to yaml)
|
|||
|
|
}
|
|||
|
|
"json" => {
|
|||
|
|
print $"Configuration ($config_type): ($config_file)"
|
|||
|
|
print ""
|
|||
|
|
($config | to json)
|
|||
|
|
}
|
|||
|
|
"toml" => {
|
|||
|
|
print $"Configuration ($config_type): ($config_file)"
|
|||
|
|
print ""
|
|||
|
|
($config | to toml)
|
|||
|
|
}
|
|||
|
|
"nickel" => {
|
|||
|
|
if ($config_file | str ends-with ".ncl") {
|
|||
|
|
open $config_file
|
|||
|
|
} else {
|
|||
|
|
print "ℹ️ Configuration is stored in YAML format, not Nickel"
|
|||
|
|
print " Use --format=yaml to view the config"
|
|||
|
|
($config | to json)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_ => ($config | to yaml)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Validate workspace configuration
|
|||
|
|
export def "workspace-config-validate" [
|
|||
|
|
workspace_name?: string
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
print $"🔍 Validating workspace: ($workspace.name)"
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
mut all_valid = true
|
|||
|
|
|
|||
|
|
let config_dir = ($workspace.path | path join "config")
|
|||
|
|
let decl_file = ($config_dir | path join "provisioning.ncl")
|
|||
|
|
let yaml_file = ($config_dir | path join "provisioning.yaml")
|
|||
|
|
|
|||
|
|
let config_file = if ($decl_file | path exists) {
|
|||
|
|
let ncl_ok = (ncl-eval-soft $decl_file [] null | is-not-empty)
|
|||
|
|
if $ncl_ok {
|
|||
|
|
$decl_file
|
|||
|
|
} else if ($yaml_file | path exists) {
|
|||
|
|
$yaml_file
|
|||
|
|
} else {
|
|||
|
|
$decl_file
|
|||
|
|
}
|
|||
|
|
} else if ($yaml_file | path exists) {
|
|||
|
|
$yaml_file
|
|||
|
|
} else {
|
|||
|
|
null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($config_file | is-empty) {
|
|||
|
|
print "✓ Main config: (not found)"
|
|||
|
|
print " ❌ No Nickel (.ncl) or YAML (.yaml) config file found"
|
|||
|
|
$all_valid = false
|
|||
|
|
} else {
|
|||
|
|
let config_type = if ($config_file | str ends-with ".ncl") { "Nickel" } else { "YAML" }
|
|||
|
|
print $"✓ Main config: ($config_file) [($config_type)]"
|
|||
|
|
|
|||
|
|
let config = if ($config_file | str ends-with ".ncl") {
|
|||
|
|
let file_dir = ($config_file | path dirname)
|
|||
|
|
let file_name = ($config_file | path basename)
|
|||
|
|
let decl_mod_exists = (($file_dir | path join "nickel.mod") | path exists)
|
|||
|
|
|
|||
|
|
let parsed = if $decl_mod_exists {
|
|||
|
|
let res = (do { ^sh -c $"cd '($file_dir)' && nickel export ($file_name) --format json" } | complete)
|
|||
|
|
if ($res.stdout | is-not-empty) { $res.stdout | from json } else { null }
|
|||
|
|
} else {
|
|||
|
|
ncl-eval-soft $config_file [] null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($parsed | is-empty) {
|
|||
|
|
print $" ❌ Nickel compilation failed, but YAML fallback not available"
|
|||
|
|
$all_valid = false
|
|||
|
|
{}
|
|||
|
|
} else {
|
|||
|
|
if (($parsed | columns) | any { |col| $col == "workspace_config" }) {
|
|||
|
|
$parsed.workspace_config
|
|||
|
|
} else {
|
|||
|
|
$parsed
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
open $config_file
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($config | is-not-empty) {
|
|||
|
|
if ($config_file | str ends-with ".ncl") {
|
|||
|
|
print " ✅ Valid Nickel (schema validated)"
|
|||
|
|
} else {
|
|||
|
|
print " ✅ Valid YAML"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($config | get workspace? | default null | is-empty) {
|
|||
|
|
print $" ⚠️ Missing 'workspace' section"
|
|||
|
|
}
|
|||
|
|
if ($config | get paths? | default null | is-empty) {
|
|||
|
|
print $" ⚠️ Missing 'paths' section"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print ""
|
|||
|
|
print "Providers:"
|
|||
|
|
let providers_dir = ($workspace.path | path join "config" | path join "providers")
|
|||
|
|
if ($providers_dir | path exists) {
|
|||
|
|
let provider_files = (glob $"($providers_dir)/*.toml")
|
|||
|
|
if ($provider_files | is-empty) {
|
|||
|
|
print " ⚠️ No provider configs found"
|
|||
|
|
} else {
|
|||
|
|
for file in $provider_files {
|
|||
|
|
let provider_name = ($file | path basename | str replace ".toml" "")
|
|||
|
|
print $" ✓ ($provider_name): ($file)"
|
|||
|
|
let _ = (open $file)
|
|||
|
|
print $" ✅ Valid TOML"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ No providers directory"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print ""
|
|||
|
|
print "Platform Services:"
|
|||
|
|
let platform_dir = ($workspace.path | path join "config" | path join "platform")
|
|||
|
|
if ($platform_dir | path exists) {
|
|||
|
|
let platform_files = (glob $"($platform_dir)/*.toml")
|
|||
|
|
if ($platform_files | is-empty) {
|
|||
|
|
print " ⚠️ No platform service configs found"
|
|||
|
|
} else {
|
|||
|
|
for file in $platform_files {
|
|||
|
|
let service_name = ($file | path basename | str replace ".toml" "")
|
|||
|
|
print $" ✓ ($service_name): ($file)"
|
|||
|
|
let _ = (open $file)
|
|||
|
|
print $" ✅ Valid TOML"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ No platform services directory"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print ""
|
|||
|
|
print "KMS Configuration:"
|
|||
|
|
let kms_file = ($workspace.path | path join "config" | path join "kms.toml")
|
|||
|
|
if ($kms_file | path exists) {
|
|||
|
|
print $" ✓ KMS config: ($kms_file)"
|
|||
|
|
let _ = (open $kms_file)
|
|||
|
|
print $" ✅ Valid TOML"
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ KMS not configured"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print ""
|
|||
|
|
if $all_valid {
|
|||
|
|
print "✅ Validation complete - all configs are valid"
|
|||
|
|
} else {
|
|||
|
|
print "❌ Validation failed - some configs have errors"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$all_valid
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Generate provider config for workspace
|
|||
|
|
export def "workspace-config-generate-provider" [
|
|||
|
|
provider_name: string
|
|||
|
|
workspace_name?: string
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
let provisioning_path = $env.PROVISIONING_PATH?
|
|||
|
|
if ($provisioning_path | is-empty) {
|
|||
|
|
print "❌ PROVISIONING_PATH environment variable not set"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let template_dir = ($provisioning_path | path join "catalog" | path join "providers" | path join $provider_name)
|
|||
|
|
let template_file = ($template_dir | path join "config.defaults.toml")
|
|||
|
|
|
|||
|
|
if not ($template_file | path exists) {
|
|||
|
|
print "❌ Provider template not found: ($provider_name)"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let output_dir = ($workspace.path | path join "config" | path join "providers")
|
|||
|
|
mkdir $output_dir
|
|||
|
|
|
|||
|
|
let output_file = ($output_dir | path join $"($provider_name).toml")
|
|||
|
|
|
|||
|
|
let template = (open $template_file)
|
|||
|
|
let content = (
|
|||
|
|
$template
|
|||
|
|
| str replace --all "{{workspace.path}}" $workspace.path
|
|||
|
|
| str replace --all "{{workspace.name}}" $workspace.name
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
$content | save $output_file
|
|||
|
|
|
|||
|
|
print $"✅ Provider config generated: ($output_file)"
|
|||
|
|
print $"💡 Edit the file to customize settings"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Edit configuration file
|
|||
|
|
export def "workspace-config-edit" [
|
|||
|
|
config_type: string # provider, platform, kms, main
|
|||
|
|
name?: string # provider/platform name (not needed for kms/main)
|
|||
|
|
workspace_name?: string
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
let config_file = match $config_type {
|
|||
|
|
"main" => ($workspace.path | path join "config" | path join "provisioning.yaml")
|
|||
|
|
"provider" => {
|
|||
|
|
if ($name | is-empty) {
|
|||
|
|
print "❌ Provider name required"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
($workspace.path | path join "config" | path join "providers" | path join $"($name).toml")
|
|||
|
|
}
|
|||
|
|
"platform" => {
|
|||
|
|
if ($name | is-empty) {
|
|||
|
|
print "❌ Platform service name required"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
($workspace.path | path join "config" | path join "platform" | path join $"($name).toml")
|
|||
|
|
}
|
|||
|
|
"kms" => ($workspace.path | path join "config" | path join "kms.toml")
|
|||
|
|
_ => {
|
|||
|
|
print "❌ Unknown config type: ($config_type)"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if not ($config_file | path exists) {
|
|||
|
|
print "❌ Config file not found: ($config_file)"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let editor = ($env.EDITOR? | default "vi")
|
|||
|
|
^$editor $config_file
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Show configuration hierarchy
|
|||
|
|
export def "workspace-config-hierarchy" [
|
|||
|
|
workspace_name?: string
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
print $"📊 Configuration Hierarchy for: ($workspace.name)"
|
|||
|
|
print "=================================================="
|
|||
|
|
print ""
|
|||
|
|
print "Priority (highest to lowest):"
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
print "5. Environment Variables (highest)"
|
|||
|
|
print " PROVISIONING_*"
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
print "4. User Context"
|
|||
|
|
let user_config = ((get-user-config-dir) | path join $"ws_($workspace.name).yaml")
|
|||
|
|
print $" ($user_config)"
|
|||
|
|
if ($user_config | path exists) {
|
|||
|
|
print " ✅ Exists"
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ Not found"
|
|||
|
|
}
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
print "3. Platform Services"
|
|||
|
|
let platform_dir = ($workspace.path | path join "config" | path join "platform")
|
|||
|
|
print $" ($platform_dir)/"
|
|||
|
|
if ($platform_dir | path exists) {
|
|||
|
|
let files = (ls $"($platform_dir)/*.toml" | get name)
|
|||
|
|
if ($files | is-empty) {
|
|||
|
|
print " ⚠️ No platform configs"
|
|||
|
|
} else {
|
|||
|
|
for file in $files {
|
|||
|
|
print $" • ($file | path basename)"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ No platform configs"
|
|||
|
|
}
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
print "2. Provider Configs"
|
|||
|
|
let providers_dir = ($workspace.path | path join "config" | path join "providers")
|
|||
|
|
print $" ($providers_dir)/"
|
|||
|
|
if ($providers_dir | path exists) {
|
|||
|
|
let files = (ls $"($providers_dir)/*.toml" | get name)
|
|||
|
|
if ($files | is-empty) {
|
|||
|
|
print " ⚠️ No provider configs"
|
|||
|
|
} else {
|
|||
|
|
for file in $files {
|
|||
|
|
print $" • ($file | path basename)"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ No provider configs"
|
|||
|
|
}
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
print "1. Workspace Config (lowest)"
|
|||
|
|
let main_config = ($workspace.path | path join "config" | path join "provisioning.yaml")
|
|||
|
|
print $" ($main_config)"
|
|||
|
|
if ($main_config | path exists) {
|
|||
|
|
print " ✅ Exists"
|
|||
|
|
} else {
|
|||
|
|
print " ❌ Not found"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# List all configuration files for workspace
|
|||
|
|
export def "workspace-config-list" [
|
|||
|
|
workspace_name?: string
|
|||
|
|
--type: string = "all" # all, provider, platform, kms, main
|
|||
|
|
] {
|
|||
|
|
let workspace = (get-workspace-context $workspace_name)
|
|||
|
|
|
|||
|
|
print $"📋 Configuration Files for: ($workspace.name)"
|
|||
|
|
print "=================================================="
|
|||
|
|
print ""
|
|||
|
|
|
|||
|
|
mut configs = []
|
|||
|
|
|
|||
|
|
if ($type == "all" or $type == "main") {
|
|||
|
|
let main_config = ($workspace.path | path join "config" | path join "provisioning.yaml")
|
|||
|
|
if ($main_config | path exists) {
|
|||
|
|
$configs = ($configs | append {
|
|||
|
|
type: "main"
|
|||
|
|
name: "provisioning.yaml"
|
|||
|
|
path: $main_config
|
|||
|
|
exists: true
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($type == "all" or $type == "provider") {
|
|||
|
|
let providers_dir = ($workspace.path | path join "config" | path join "providers")
|
|||
|
|
if ($providers_dir | path exists) {
|
|||
|
|
let provider_files = (ls $"($providers_dir)/*.toml" | get name)
|
|||
|
|
for file in $provider_files {
|
|||
|
|
$configs = ($configs | append {
|
|||
|
|
type: "provider"
|
|||
|
|
name: ($file | path basename | str replace ".toml" "")
|
|||
|
|
path: $file
|
|||
|
|
exists: true
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($type == "all" or $type == "platform") {
|
|||
|
|
let platform_dir = ($workspace.path | path join "config" | path join "platform")
|
|||
|
|
if ($platform_dir | path exists) {
|
|||
|
|
let platform_files = (ls $"($platform_dir)/*.toml" | get name)
|
|||
|
|
for file in $platform_files {
|
|||
|
|
$configs = ($configs | append {
|
|||
|
|
type: "platform"
|
|||
|
|
name: ($file | path basename | str replace ".toml" "")
|
|||
|
|
path: $file
|
|||
|
|
exists: true
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($type == "all" or $type == "kms") {
|
|||
|
|
let kms_file = ($workspace.path | path join "config" | path join "kms.toml")
|
|||
|
|
if ($kms_file | path exists) {
|
|||
|
|
$configs = ($configs | append {
|
|||
|
|
type: "kms"
|
|||
|
|
name: "kms"
|
|||
|
|
path: $kms_file
|
|||
|
|
exists: true
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$configs | table
|
|||
|
|
}
|