Update core components including CLI, Nushell libraries, plugins system, and utility scripts for the provisioning system. CLI Updates: - Command implementations - CLI utilities and dispatching - Help system improvements - Command validation Library Updates: - Configuration management system - Infrastructure validation - Extension system improvements - Secrets management - Workspace operations - Cache management system Plugin System: - Interactive form plugin (inquire) - KCL integration plugin - Performance optimization plugins - Plugin registration system Utilities: - Build and distribution scripts - Installation procedures - Testing utilities - Development tools Documentation: - Library module documentation - Extension API guides - Plugin usage guides - Service management documentation All changes are backward compatible. No breaking changes.
541 lines
16 KiB
Plaintext
541 lines
16 KiB
Plaintext
#!/usr/bin/env nu
|
|
# [command]
|
|
# name = "forminquire integration"
|
|
# group = "infrastructure"
|
|
# tags = ["forminquire", "forms", "interactive", "templates"]
|
|
# version = "1.0.0"
|
|
# requires = ["nu_plugin_tera", "forminquire:1.0.0"]
|
|
# note = "Dynamic form generation using Jinja2 templates rendered with nu_plugin_tera"
|
|
|
|
# ============================================================================
|
|
# FormInquire Integration System
|
|
# Version: 1.0.0
|
|
# Purpose: Generate interactive forms dynamically from templates and config data
|
|
# ============================================================================
|
|
|
|
# Get form cache directory
|
|
def get-form-cache-dir [] : nothing -> string {
|
|
let cache_dir = (
|
|
if ($env.XDG_CACHE_HOME? | is-empty) {
|
|
$"($env.HOME)/.cache/provisioning/forms"
|
|
} else {
|
|
$"($env.XDG_CACHE_HOME)/provisioning/forms"
|
|
}
|
|
)
|
|
$cache_dir
|
|
}
|
|
|
|
# Ensure cache directory exists
|
|
def ensure-form-cache-dir [] : nothing -> string {
|
|
let cache_dir = (get-form-cache-dir)
|
|
let _mkdir_result = (do {
|
|
if not (($cache_dir | path exists)) {
|
|
^mkdir -p $cache_dir
|
|
}
|
|
} | complete)
|
|
$cache_dir
|
|
}
|
|
|
|
# Get template directory
|
|
def get-template-dir [] : nothing -> string {
|
|
let proj_root = (
|
|
if (($env.PROVISIONING_ROOT? | is-empty)) {
|
|
$"($env.HOME)/project-provisioning"
|
|
} else {
|
|
$env.PROVISIONING_ROOT
|
|
}
|
|
)
|
|
$"($proj_root)/provisioning/core/forminquire/templates"
|
|
}
|
|
|
|
# Load TOML configuration file
|
|
def load-toml-config [path: string] : nothing -> record {
|
|
let result = (do { open $path | from toml } | complete)
|
|
if ($result.exit_code == 0) {
|
|
$result.stdout
|
|
} else {
|
|
{}
|
|
}
|
|
}
|
|
|
|
# Load YAML configuration file
|
|
def load-yaml-config [path: string] : nothing -> record {
|
|
let result = (do { open $path | from yaml } | complete)
|
|
if ($result.exit_code == 0) {
|
|
$result.stdout
|
|
} else {
|
|
{}
|
|
}
|
|
}
|
|
|
|
# Render Jinja2 template with data
|
|
export def render-template [
|
|
template_name: string
|
|
data: record = {}
|
|
] : nothing -> record {
|
|
let template_dir = (get-template-dir)
|
|
let template_path = $"($template_dir)/($template_name).j2"
|
|
|
|
if not (($template_path | path exists)) {
|
|
return {
|
|
error: $"Template not found: ($template_path)"
|
|
content: ""
|
|
}
|
|
}
|
|
|
|
let template_content_result = (do { ^cat $template_path } | complete)
|
|
if ($template_content_result.exit_code != 0) {
|
|
return {
|
|
error: "Failed to read template file"
|
|
content: ""
|
|
}
|
|
}
|
|
|
|
let template_content = $template_content_result.stdout
|
|
|
|
let enriched_data = (
|
|
$data
|
|
| merge {
|
|
now_iso: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
|
|
home_dir: $env.HOME
|
|
username: (whoami)
|
|
provisioning_root: (
|
|
if (($env.PROVISIONING_ROOT? | is-empty)) {
|
|
$"($env.HOME)/project-provisioning"
|
|
} else {
|
|
$env.PROVISIONING_ROOT
|
|
}
|
|
)
|
|
}
|
|
)
|
|
|
|
let render_result = (do {
|
|
tera -t $template_content --data ($enriched_data | to json)
|
|
} | complete)
|
|
|
|
if ($render_result.exit_code == 0) {
|
|
{
|
|
error: ""
|
|
content: $render_result.stdout
|
|
}
|
|
} else {
|
|
{
|
|
error: "Template rendering failed"
|
|
content: ""
|
|
}
|
|
}
|
|
}
|
|
|
|
# Generate form from template and save to cache
|
|
export def generate-form [
|
|
form_name: string
|
|
template_name: string
|
|
data: record = {}
|
|
] : nothing -> record {
|
|
let cache_dir = (ensure-form-cache-dir)
|
|
let form_path = $"($cache_dir)/($form_name).toml"
|
|
|
|
let render_result = (render-template $template_name $data)
|
|
|
|
if not (($render_result.error | is-empty)) {
|
|
return {
|
|
success: false
|
|
error: $render_result.error
|
|
form_path: ""
|
|
}
|
|
}
|
|
|
|
let write_result = (do {
|
|
$render_result.content | ^tee $form_path > /dev/null
|
|
} | complete)
|
|
|
|
if ($write_result.exit_code == 0) {
|
|
{
|
|
success: true
|
|
error: ""
|
|
form_path: $form_path
|
|
}
|
|
} else {
|
|
{
|
|
success: false
|
|
error: "Failed to write form file"
|
|
form_path: ""
|
|
}
|
|
}
|
|
}
|
|
|
|
# Execute FormInquire with generated form
|
|
export def run-form [form_path: string] : nothing -> record {
|
|
if not (($form_path | path exists)) {
|
|
return {
|
|
success: false
|
|
error: $"Form file not found: ($form_path)"
|
|
values: {}
|
|
}
|
|
}
|
|
|
|
let forminquire_result = (do {
|
|
^forminquire --from-file $form_path --output json
|
|
} | complete)
|
|
|
|
if ($forminquire_result.exit_code != 0) {
|
|
return {
|
|
success: false
|
|
error: "FormInquire execution failed"
|
|
values: {}
|
|
}
|
|
}
|
|
|
|
let parse_result = (do {
|
|
$forminquire_result.stdout | from json
|
|
} | complete)
|
|
|
|
if ($parse_result.exit_code == 0) {
|
|
{
|
|
success: true
|
|
error: ""
|
|
values: $parse_result.stdout
|
|
}
|
|
} else {
|
|
{
|
|
success: false
|
|
error: "Failed to parse FormInquire output"
|
|
values: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Complete flow: generate form from template and run it
|
|
export def interactive-form [
|
|
form_name: string
|
|
template_name: string
|
|
data: record = {}
|
|
] : nothing -> record {
|
|
let generate_result = (generate-form $form_name $template_name $data)
|
|
|
|
if not $generate_result.success {
|
|
return {
|
|
success: false
|
|
error: $generate_result.error
|
|
form_path: ""
|
|
values: {}
|
|
}
|
|
}
|
|
|
|
let run_result = (run-form $generate_result.form_path)
|
|
|
|
{
|
|
success: $run_result.success
|
|
error: $run_result.error
|
|
form_path: $generate_result.form_path
|
|
values: $run_result.values
|
|
}
|
|
}
|
|
|
|
# Load user preferences from config
|
|
export def load-user-preferences [] : nothing -> record {
|
|
let config_path = $"($env.HOME)/.config/provisioning/user_config.yaml"
|
|
load-yaml-config $config_path
|
|
}
|
|
|
|
# Load workspace config
|
|
export def load-workspace-config [workspace_name: string] : nothing -> record {
|
|
let workspace_dir = (
|
|
if (($env.PROVISIONING_WORKSPACE? | is-empty)) {
|
|
$"($env.HOME)/workspaces/($workspace_name)"
|
|
} else {
|
|
$env.PROVISIONING_WORKSPACE
|
|
}
|
|
)
|
|
|
|
let config_file = $"($workspace_dir)/config.toml"
|
|
load-toml-config $config_file
|
|
}
|
|
|
|
# Load system defaults
|
|
export def load-system-defaults [] : nothing -> record {
|
|
let proj_root = (
|
|
if (($env.PROVISIONING_ROOT? | is-empty)) {
|
|
$"($env.HOME)/project-provisioning"
|
|
} else {
|
|
$env.PROVISIONING_ROOT
|
|
}
|
|
)
|
|
|
|
let defaults_file = $"($proj_root)/provisioning/config/config.defaults.toml"
|
|
load-toml-config $defaults_file
|
|
}
|
|
|
|
# Merge multiple config sources with priority
|
|
export def merge-config-sources [
|
|
defaults: record = {}
|
|
workspace: record = {}
|
|
user: record = {}
|
|
overrides: record = {}
|
|
] : nothing -> record {
|
|
$defaults | merge $workspace | merge $user | merge $overrides
|
|
}
|
|
|
|
# Get form context with all available data
|
|
export def get-form-context [
|
|
workspace_name: string = ""
|
|
custom_data: record = {}
|
|
] : nothing -> record {
|
|
let defaults = (load-system-defaults)
|
|
let user_prefs = (load-user-preferences)
|
|
|
|
let workspace_config = (
|
|
if (($workspace_name | is-empty)) {
|
|
{}
|
|
} else {
|
|
load-workspace-config $workspace_name
|
|
}
|
|
)
|
|
|
|
let merged = (merge-config-sources $defaults $workspace_config $user_prefs $custom_data)
|
|
$merged
|
|
}
|
|
|
|
# Settings update form - loads current settings as defaults
|
|
export def settings-update-form [] : nothing -> record {
|
|
let context = (get-form-context)
|
|
|
|
let data = {
|
|
config_source: "system defaults + user preferences"
|
|
editor: ($context.preferences.editor? // "vim")
|
|
output_format: ($context.preferences.output_format? // "yaml")
|
|
default_log_level: ($context.preferences.default_log_level? // "info")
|
|
preferred_provider: ($context.preferences.preferred_provider? // "upcloud")
|
|
confirm_delete: ($context.preferences.confirm_delete? // true)
|
|
confirm_deploy: ($context.preferences.confirm_deploy? // true)
|
|
}
|
|
|
|
interactive-form "settings-update" "settings-update" $data
|
|
}
|
|
|
|
# Setup wizard form
|
|
export def setup-wizard-form [] : nothing -> record {
|
|
let context = (get-form-context)
|
|
|
|
let data = {
|
|
system_name: ($context.system_name? // "provisioning")
|
|
admin_email: ($context.admin_email? // "")
|
|
deployment_mode: ($context.deployment_mode? // "solo")
|
|
infrastructure_provider: ($context.infrastructure_provider? // "upcloud")
|
|
cpu_cores: ($context.resources.cpu_cores? // "4")
|
|
memory_gb: ($context.resources.memory_gb? // "8")
|
|
disk_gb: ($context.resources.disk_gb? // "50")
|
|
workspace_path: ($context.workspace_path? // $"($env.HOME)/provisioning-workspace")
|
|
}
|
|
|
|
interactive-form "setup-wizard" "setup-wizard" $data
|
|
}
|
|
|
|
# Workspace init form
|
|
export def workspace-init-form [workspace_name: string = ""] : nothing -> record {
|
|
let context = (get-form-context $workspace_name)
|
|
|
|
let data = {
|
|
workspace_name: (
|
|
if (($workspace_name | is-empty)) {
|
|
"default"
|
|
} else {
|
|
$workspace_name
|
|
}
|
|
)
|
|
workspace_description: ($context.description? // "")
|
|
workspace_path: ($context.path? // $"($env.HOME)/workspaces/($workspace_name)")
|
|
default_provider: ($context.default_provider? // "upcloud")
|
|
default_region: ($context.default_region? // "")
|
|
init_git: ($context.init_git? // true)
|
|
create_example_configs: ($context.create_example_configs? // true)
|
|
setup_secrets: ($context.setup_secrets? // true)
|
|
enable_testing: ($context.enable_testing? // true)
|
|
enable_monitoring: ($context.enable_monitoring? // false)
|
|
enable_orchestrator: ($context.enable_orchestrator? // true)
|
|
}
|
|
|
|
interactive-form "workspace-init" "workspace-init" $data
|
|
}
|
|
|
|
# Server delete confirmation form
|
|
export def server-delete-confirm-form [
|
|
server_name: string
|
|
server_ip: string = ""
|
|
server_status: string = ""
|
|
] : nothing -> record {
|
|
let data = {
|
|
server_name: $server_name
|
|
server_ip: $server_ip
|
|
server_status: $server_status
|
|
}
|
|
|
|
interactive-form "server-delete-confirm" "server-delete-confirm" $data
|
|
}
|
|
|
|
# Clean up old form files from cache (older than 1 day)
|
|
export def cleanup-form-cache [] : nothing -> record {
|
|
let cache_dir = (get-form-cache-dir)
|
|
|
|
if not (($cache_dir | path exists)) {
|
|
return {cleaned: 0, error: ""}
|
|
}
|
|
|
|
let find_result = (do {
|
|
^find $cache_dir -name "*.toml" -type f -mtime +1 -delete
|
|
} | complete)
|
|
|
|
{cleaned: 0, error: ""}
|
|
}
|
|
|
|
# List available templates
|
|
export def list-templates [] : nothing -> list {
|
|
let template_dir = (get-template-dir)
|
|
|
|
if not (($template_dir | path exists)) {
|
|
return []
|
|
}
|
|
|
|
let find_result = (do {
|
|
^find $template_dir -name "*.j2" -type f
|
|
} | complete)
|
|
|
|
if ($find_result.exit_code == 0) {
|
|
$find_result.stdout
|
|
| lines
|
|
| each {|path|
|
|
let name = ($path | path basename | str replace ".j2" "")
|
|
{
|
|
name: $name
|
|
path: $path
|
|
template_file: ($path | path basename)
|
|
}
|
|
}
|
|
} else {
|
|
[]
|
|
}
|
|
}
|
|
|
|
# List generated forms in cache
|
|
export def list-cached-forms [] : nothing -> list {
|
|
let cache_dir = (ensure-form-cache-dir)
|
|
|
|
let find_result = (do {
|
|
^find $cache_dir -name "*.toml" -type f
|
|
} | complete)
|
|
|
|
if ($find_result.exit_code == 0) {
|
|
$find_result.stdout
|
|
| lines
|
|
| each {|path|
|
|
{
|
|
name: ($path | path basename)
|
|
path: $path
|
|
}
|
|
}
|
|
} else {
|
|
[]
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# DELETE CONFIRMATION HELPERS
|
|
# ============================================================================
|
|
|
|
# Run server delete confirmation
|
|
export def server-delete-confirm [
|
|
server_name: string
|
|
server_ip?: string
|
|
server_status?: string
|
|
] : nothing -> record {
|
|
let context = {
|
|
server_name: $server_name
|
|
server_ip: (if ($server_ip | is-empty) { "" } else { $server_ip })
|
|
server_status: (if ($server_status | is-empty) { "running" } else { $server_status })
|
|
}
|
|
|
|
run-forminquire-form "provisioning/core/shlib/forms/infrastructure/server_delete_confirm.toml" $context
|
|
}
|
|
|
|
# Run taskserv delete confirmation
|
|
export def taskserv-delete-confirm [
|
|
taskserv_name: string
|
|
taskserv_type?: string
|
|
taskserv_server?: string
|
|
taskserv_status?: string
|
|
dependent_services?: string
|
|
] : nothing -> record {
|
|
let context = {
|
|
taskserv_name: $taskserv_name
|
|
taskserv_type: (if ($taskserv_type | is-empty) { "" } else { $taskserv_type })
|
|
taskserv_server: (if ($taskserv_server | is-empty) { "" } else { $taskserv_server })
|
|
taskserv_status: (if ($taskserv_status | is-empty) { "unknown" } else { $taskserv_status })
|
|
dependent_services: (if ($dependent_services | is-empty) { "none" } else { $dependent_services })
|
|
}
|
|
|
|
run-forminquire-form "provisioning/core/shlib/forms/infrastructure/taskserv_delete_confirm.toml" $context
|
|
}
|
|
|
|
# Run cluster delete confirmation
|
|
export def cluster-delete-confirm [
|
|
cluster_name: string
|
|
cluster_type?: string
|
|
node_count?: string
|
|
total_resources?: string
|
|
deployments_count?: string
|
|
services_count?: string
|
|
volumes_count?: string
|
|
] : nothing -> record {
|
|
let context = {
|
|
cluster_name: $cluster_name
|
|
cluster_type: (if ($cluster_type | is-empty) { "" } else { $cluster_type })
|
|
node_count: (if ($node_count | is-empty) { "unknown" } else { $node_count })
|
|
total_resources: (if ($total_resources | is-empty) { "" } else { $total_resources })
|
|
deployments_count: (if ($deployments_count | is-empty) { "0" } else { $deployments_count })
|
|
services_count: (if ($services_count | is-empty) { "0" } else { $services_count })
|
|
volumes_count: (if ($volumes_count | is-empty) { "0" } else { $volumes_count })
|
|
}
|
|
|
|
run-forminquire-form "provisioning/core/shlib/forms/infrastructure/cluster_delete_confirm.toml" $context
|
|
}
|
|
|
|
# Generic delete confirmation
|
|
export def generic-delete-confirm [
|
|
resource_type: string
|
|
resource_name: string
|
|
resource_id?: string
|
|
resource_status?: string
|
|
] : nothing -> record {
|
|
let context = {
|
|
resource_type: $resource_type
|
|
resource_name: $resource_name
|
|
resource_id: (if ($resource_id | is-empty) { "" } else { $resource_id })
|
|
resource_status: (if ($resource_status | is-empty) { "unknown" } else { $resource_status })
|
|
}
|
|
|
|
run-forminquire-form "provisioning/core/shlib/forms/infrastructure/generic_delete_confirm.toml" $context
|
|
}
|
|
|
|
# Validate delete confirmation result
|
|
export def validate-delete-confirmation [result: record] : nothing -> bool {
|
|
# Must have success = true
|
|
let success = ($result.success // false)
|
|
if not $success {
|
|
return false
|
|
}
|
|
|
|
let values = ($result.values // {})
|
|
|
|
# Must have typed "DELETE" or "DELETE CLUSTER"
|
|
let confirm_text = ($values.confirmation_text // "")
|
|
let is_confirmed = (($confirm_text == "DELETE") or ($confirm_text == "DELETE CLUSTER"))
|
|
|
|
# Must have checked final confirmation checkbox
|
|
let final_checked = ($values.final_confirm // false)
|
|
|
|
# Must have checked proceed checkbox
|
|
let proceed_checked = ($values.proceed // false)
|
|
|
|
($is_confirmed and $final_checked and $proceed_checked)
|
|
}
|