chore: update items
This commit is contained in:
parent
3904d2fbc7
commit
a327f59bf7
@ -1,239 +0,0 @@
|
|||||||
# FormInquire Integration System
|
|
||||||
|
|
||||||
Dynamic form generation using Jinja2 templates rendered with `nu_plugin_tera`.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
provisioning/core/forminquire/
|
|
||||||
├── templates/ # Jinja2 form templates (.j2)
|
|
||||||
│ ├── setup-wizard.form.j2
|
|
||||||
│ ├── workspace-init.form.j2
|
|
||||||
│ ├── settings-update.form.j2
|
|
||||||
│ ├── server-delete-confirm.form.j2
|
|
||||||
│ └── ...more templates
|
|
||||||
├── nulib/
|
|
||||||
│ └── forminquire.nu # Nushell integration functions
|
|
||||||
└── wrappers/
|
|
||||||
└── form.sh # Bash wrapper for FormInquire
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
1. **Template Rendering**: Jinja2 templates are rendered with data from config files
|
|
||||||
2. **Form Generation**: Rendered templates are saved as TOML forms in cache
|
|
||||||
3. **User Interaction**: FormInquire binary presents the form to user
|
|
||||||
4. **Result Processing**: JSON output from FormInquire is returned to calling code
|
|
||||||
|
|
||||||
```
|
|
||||||
Config Data → Template Rendering → Form Generation → FormInquire → JSON Output
|
|
||||||
(nu_plugin_tera) (cache: ~/.cache/) (interactive)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Examples
|
|
||||||
|
|
||||||
### Settings Update with Current Values as Defaults
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
use provisioning/core/forminquire/nulib/forminquire.nu *
|
|
||||||
|
|
||||||
# Load current settings and show form with them as defaults
|
|
||||||
let result = (settings-update-form)
|
|
||||||
|
|
||||||
if $result.success {
|
|
||||||
# Process updated settings
|
|
||||||
print $"Updated: ($result.values | to json)"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setup Wizard
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
let result = (setup-wizard-form)
|
|
||||||
|
|
||||||
if $result.success {
|
|
||||||
print "Setup configuration:"
|
|
||||||
print ($result.values | to json)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workspace Initialization
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
let result = (workspace-init-form "my-workspace")
|
|
||||||
|
|
||||||
if $result.success {
|
|
||||||
print "Workspace created with settings:"
|
|
||||||
print ($result.values | to json)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Server Delete Confirmation
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
let confirm = (server-delete-confirm-form "web-01" "192.168.1.10" "running")
|
|
||||||
|
|
||||||
if $confirm.success {
|
|
||||||
let confirmation_text = $confirm.values.confirmation_text
|
|
||||||
let final_confirm = $confirm.values.final_confirm
|
|
||||||
|
|
||||||
if ($confirmation_text == "web-01" and $final_confirm) {
|
|
||||||
print "Deleting server..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Template Variables
|
|
||||||
|
|
||||||
All templates have access to:
|
|
||||||
|
|
||||||
### Automatic Variables (always available)
|
|
||||||
- `now_iso`: Current timestamp in ISO 8601 format
|
|
||||||
- `home_dir`: User's home directory
|
|
||||||
- `username`: Current username
|
|
||||||
- `provisioning_root`: Provisioning root directory
|
|
||||||
|
|
||||||
### Custom Variables (passed per form)
|
|
||||||
- Settings from `config.defaults.toml`
|
|
||||||
- User preferences from `~/.config/provisioning/user_config.yaml`
|
|
||||||
- Workspace configuration from workspace `config.toml`
|
|
||||||
- Any custom data passed to the form function
|
|
||||||
|
|
||||||
## Cache Management
|
|
||||||
|
|
||||||
Forms are cached at: `~/.cache/provisioning/forms/`
|
|
||||||
|
|
||||||
### Cleanup Old Forms
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
let cleanup_result = (cleanup-form-cache)
|
|
||||||
print $"Cleaned up ($cleanup_result.cleaned) old form files"
|
|
||||||
```
|
|
||||||
|
|
||||||
### List Generated Forms
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
list-cached-forms
|
|
||||||
```
|
|
||||||
|
|
||||||
## Template Syntax
|
|
||||||
|
|
||||||
Templates use Jinja2 syntax with macros for common form elements:
|
|
||||||
|
|
||||||
```jinja2
|
|
||||||
[items.my_field]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Enter value"
|
|
||||||
default = "{{ my_variable }}"
|
|
||||||
help = "Help text here"
|
|
||||||
required = true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Available Form Types
|
|
||||||
|
|
||||||
- `text`: Text input
|
|
||||||
- `select`: Dropdown selection
|
|
||||||
- `confirm`: Yes/No confirmation
|
|
||||||
- `password`: Masked password input
|
|
||||||
- `multiselect`: Multiple selection
|
|
||||||
|
|
||||||
## Available Functions
|
|
||||||
|
|
||||||
### Form Execution
|
|
||||||
|
|
||||||
- `interactive-form [name] [template] [data]` - Complete form flow
|
|
||||||
- `render-template [template_name] [data]` - Render template only
|
|
||||||
- `generate-form [form_name] [template_name] [data]` - Generate TOML form
|
|
||||||
- `run-form [form_path]` - Execute FormInquire with form
|
|
||||||
|
|
||||||
### Config Loading
|
|
||||||
|
|
||||||
- `load-user-preferences` - Load user preferences from config
|
|
||||||
- `load-workspace-config [workspace_name]` - Load workspace settings
|
|
||||||
- `load-system-defaults` - Load system defaults
|
|
||||||
- `get-form-context [workspace_name] [custom_data]` - Merged config context
|
|
||||||
|
|
||||||
### Convenience Functions
|
|
||||||
|
|
||||||
- `settings-update-form` - Update system settings
|
|
||||||
- `setup-wizard-form` - Run setup wizard
|
|
||||||
- `workspace-init-form [name]` - Initialize workspace
|
|
||||||
- `server-delete-confirm-form [name] [ip] [status]` - Delete confirmation
|
|
||||||
|
|
||||||
### Utilities
|
|
||||||
|
|
||||||
- `list-templates` - List available templates
|
|
||||||
- `list-cached-forms` - List generated forms in cache
|
|
||||||
- `cleanup-form-cache` - Remove old cached forms
|
|
||||||
|
|
||||||
## Shell Integration
|
|
||||||
|
|
||||||
Use the bash wrapper for shell scripts:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Generate form with Nushell
|
|
||||||
nu -c "use forminquire *; interactive-form 'my-form' 'my-template' {foo: 'bar'}" > /tmp/form.toml
|
|
||||||
|
|
||||||
# Or use form.sh wrapper directly
|
|
||||||
./provisioning/core/forminquire/wrappers/form.sh /path/to/form.toml json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Notes
|
|
||||||
|
|
||||||
- **First form**: ~200ms (template rendering + form generation)
|
|
||||||
- **Subsequent forms**: ~50ms (cached config loading)
|
|
||||||
- **User interaction**: Depends on FormInquire response time
|
|
||||||
- **Form cache**: Automatically cleaned after 1+ days
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- `forminquire` - FormInquire binary (in PATH)
|
|
||||||
- `nu_plugin_tera` - Nushell Jinja2 template plugin
|
|
||||||
- `Nushell 0.109.0+` - Core scripting language
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
All functions return structured results:
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
{
|
|
||||||
success: bool # Operation succeeded
|
|
||||||
error: string # Error message (empty if success)
|
|
||||||
form_path: string # Generated form path (if applicable)
|
|
||||||
values: record # FormInquire output values
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding New Forms
|
|
||||||
|
|
||||||
1. Create template in `templates/` with `.form.j2` extension
|
|
||||||
2. Create convenience function in `forminquire.nu` like `my-form-function`
|
|
||||||
3. Use in scripts: `my-form-function [args...]`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```jinja2
|
|
||||||
# templates/my-form.form.j2
|
|
||||||
[meta]
|
|
||||||
title = "My Custom Form"
|
|
||||||
[items.field1]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Enter value"
|
|
||||||
default = "{{ default_value }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
```nushell
|
|
||||||
# In forminquire.nu
|
|
||||||
export def my-form-function [default_value: string = ""] {
|
|
||||||
interactive-form "my-form" "my-form" {default_value: $default_value}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- Template rendering uses Jinja2 syntax only
|
|
||||||
- FormInquire must be in PATH
|
|
||||||
- `nu_plugin_tera` must be installed for template rendering
|
|
||||||
- Form output limited to FormInquire-supported types
|
|
||||||
@ -1,540 +0,0 @@
|
|||||||
#!/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)
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
# Auto-generated delete confirmation form
|
|
||||||
# Generated: {{ now_iso }}
|
|
||||||
# Server: {{ server_name }}
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
title = "Confirm Server Deletion"
|
|
||||||
description = "WARNING: This operation cannot be reversed. Please confirm carefully."
|
|
||||||
allow_cancel = true
|
|
||||||
|
|
||||||
[items.server_display]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Server to Delete"
|
|
||||||
default = "{{ server_name }}"
|
|
||||||
help = "Server name (read-only for confirmation)"
|
|
||||||
read_only = true
|
|
||||||
|
|
||||||
{% if server_ip %}
|
|
||||||
[items.server_ip]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Server IP Address"
|
|
||||||
default = "{{ server_ip }}"
|
|
||||||
help = "IP address (read-only for confirmation)"
|
|
||||||
read_only = true
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if server_status %}
|
|
||||||
[items.server_status]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Current Status"
|
|
||||||
default = "{{ server_status }}"
|
|
||||||
help = "Current server status (read-only)"
|
|
||||||
read_only = true
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
[items.confirmation_text]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Type server name to confirm deletion"
|
|
||||||
default = ""
|
|
||||||
help = "You must type the exact server name '{{ server_name }}' to proceed"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.final_confirm]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "I understand this action is irreversible. Delete server?"
|
|
||||||
help = "This will permanently delete the server and all its data"
|
|
||||||
|
|
||||||
[items.backup_before_delete]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Create backup before deletion?"
|
|
||||||
help = "Optionally create a backup of the server configuration"
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
{%- macro form_input(name, label, value="", required=false, help="") -%}
|
|
||||||
[items."{{ name }}"]
|
|
||||||
type = "text"
|
|
||||||
prompt = "{{ label }}"
|
|
||||||
default = "{{ value }}"
|
|
||||||
{% if help %}help = "{{ help }}"
|
|
||||||
{% endif %}{% if required %}required = true
|
|
||||||
{% endif %}
|
|
||||||
{%- endmacro -%}
|
|
||||||
|
|
||||||
{%- macro form_select(name, label, options=[], value="", help="") -%}
|
|
||||||
[items."{{ name }}"]
|
|
||||||
type = "select"
|
|
||||||
prompt = "{{ label }}"
|
|
||||||
options = [{% for opt in options %}"{{ opt }}"{{ "," if not loop.last }}{% endfor %}]
|
|
||||||
default = "{{ value }}"
|
|
||||||
{% if help %}help = "{{ help }}"
|
|
||||||
{% endif %}
|
|
||||||
{%- endmacro -%}
|
|
||||||
|
|
||||||
{%- macro form_confirm(name, label, help="") -%}
|
|
||||||
[items."{{ name }}"]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "{{ label }}"
|
|
||||||
{% if help %}help = "{{ help }}"
|
|
||||||
{% endif %}
|
|
||||||
{%- endmacro -%}
|
|
||||||
|
|
||||||
# Auto-generated form for settings update
|
|
||||||
# Generated: {{ now_iso }}
|
|
||||||
# Config source: {{ config_source }}
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
title = "Provisioning Settings Update"
|
|
||||||
description = "Update provisioning configuration settings"
|
|
||||||
allow_cancel = true
|
|
||||||
|
|
||||||
[items.editor]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Preferred Editor"
|
|
||||||
default = "{{ editor | default('vim') }}"
|
|
||||||
help = "Editor to use for file editing (vim, nano, emacs)"
|
|
||||||
|
|
||||||
[items.output_format]
|
|
||||||
type = "select"
|
|
||||||
prompt = "Default Output Format"
|
|
||||||
options = ["json", "yaml", "text", "table"]
|
|
||||||
default = "{{ output_format | default('yaml') }}"
|
|
||||||
help = "Default output format for commands"
|
|
||||||
|
|
||||||
[items.confirm_delete]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Confirm Destructive Operations?"
|
|
||||||
help = "Require confirmation before deleting resources"
|
|
||||||
|
|
||||||
[items.confirm_deploy]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Confirm Deployments?"
|
|
||||||
help = "Require confirmation before deploying"
|
|
||||||
|
|
||||||
[items.default_log_level]
|
|
||||||
type = "select"
|
|
||||||
prompt = "Default Log Level"
|
|
||||||
options = ["debug", "info", "warning", "error"]
|
|
||||||
default = "{{ default_log_level | default('info') }}"
|
|
||||||
help = "Default logging level"
|
|
||||||
|
|
||||||
[items.preferred_provider]
|
|
||||||
type = "select"
|
|
||||||
prompt = "Preferred Cloud Provider"
|
|
||||||
options = ["upcloud", "aws", "local"]
|
|
||||||
default = "{{ preferred_provider | default('upcloud') }}"
|
|
||||||
help = "Preferred infrastructure provider"
|
|
||||||
@ -1,180 +0,0 @@
|
|||||||
# Auto-generated form for setup wizard
|
|
||||||
# Generated: {{ now_iso }}
|
|
||||||
# This is a comprehensive 7-step setup wizard
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
title = "Provisioning System Setup Wizard"
|
|
||||||
description = "Step-by-step configuration for your infrastructure provisioning system"
|
|
||||||
allow_cancel = true
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 1: SYSTEM CONFIGURATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step1_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 1/7: System Configuration"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.config_path]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Configuration Base Path"
|
|
||||||
default = "{{ config_path | default('/etc/provisioning') }}"
|
|
||||||
help = "Where provisioning configuration will be stored"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.use_defaults_path]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Use recommended paths for your OS?"
|
|
||||||
help = "Use OS-specific default paths (recommended)"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 2: DEPLOYMENT MODE
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step2_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 2/7: Deployment Mode Selection"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.deployment_mode]
|
|
||||||
type = "select"
|
|
||||||
prompt = "How should platform services be deployed?"
|
|
||||||
options = ["docker-compose", "kubernetes", "systemd", "remote-ssh"]
|
|
||||||
default = "{{ deployment_mode | default('docker-compose') }}"
|
|
||||||
help = "Choose based on your infrastructure type"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 3: PROVIDER SELECTION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step3_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 3/7: Infrastructure Providers"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.provider_upcloud]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Use UpCloud as provider?"
|
|
||||||
help = "UpCloud offers affordable cloud VMs in European regions"
|
|
||||||
|
|
||||||
[items.provider_aws]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Use AWS as provider?"
|
|
||||||
help = "Amazon Web Services - global infrastructure"
|
|
||||||
|
|
||||||
[items.provider_hetzner]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Use Hetzner as provider?"
|
|
||||||
help = "Hetzner - German cloud provider with good pricing"
|
|
||||||
|
|
||||||
[items.provider_local]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Use Local provider?"
|
|
||||||
help = "Local deployment - useful for development and testing"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 4: RESOURCE ALLOCATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step4_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 4/7: Resource Allocation"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.cpu_count]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Number of CPUs to allocate"
|
|
||||||
default = "{{ cpu_count | default('4') }}"
|
|
||||||
help = "For cloud VMs (1-16, or more for dedicated hardware)"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.memory_gb]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Memory in GB to allocate"
|
|
||||||
default = "{{ memory_gb | default('8') }}"
|
|
||||||
help = "RAM for provisioning system and services"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.disk_gb]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Disk space in GB"
|
|
||||||
default = "{{ disk_gb | default('100') }}"
|
|
||||||
help = "Primary disk size for VMs or containers"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 5: SECURITY CONFIGURATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step5_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 5/7: Security Configuration"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.enable_mfa]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Enable Multi-Factor Authentication (MFA)?"
|
|
||||||
help = "Requires TOTP or WebAuthn for sensitive operations"
|
|
||||||
|
|
||||||
[items.enable_audit_logging]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Enable audit logging?"
|
|
||||||
help = "Log all operations for compliance and debugging"
|
|
||||||
|
|
||||||
[items.require_approval]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Require approval for destructive operations?"
|
|
||||||
help = "Prevents accidental deletion or modification"
|
|
||||||
|
|
||||||
[items.enable_tls]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Enable TLS encryption?"
|
|
||||||
help = "Use HTTPS for all API communications"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 6: WORKSPACE CONFIGURATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step6_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 6/7: Workspace Setup"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.create_workspace]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Create initial workspace now?"
|
|
||||||
help = "Create a workspace for managing your infrastructure"
|
|
||||||
|
|
||||||
[items.workspace_name]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace name"
|
|
||||||
default = "{{ workspace_name | default('default') }}"
|
|
||||||
help = "Name for your infrastructure workspace"
|
|
||||||
|
|
||||||
[items.workspace_description]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace description (optional)"
|
|
||||||
default = "{{ workspace_description | default('') }}"
|
|
||||||
help = "Brief description of what this workspace manages"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# STEP 7: REVIEW & CONFIRM
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.step7_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "STEP 7/7: Review Configuration"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.review_config]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Review the configuration summary above and confirm?"
|
|
||||||
help = "Verify all settings before applying"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.final_confirm]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "I understand this is a major configuration change. Proceed?"
|
|
||||||
help = "This will create/update system configuration files"
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
# Auto-generated form for workspace initialization
|
|
||||||
# Generated: {{ now_iso }}
|
|
||||||
|
|
||||||
[meta]
|
|
||||||
title = "Initialize New Workspace"
|
|
||||||
description = "Create and configure a new provisioning workspace for managing your infrastructure"
|
|
||||||
allow_cancel = true
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# WORKSPACE BASIC INFORMATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.workspace_info_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace Basic Information"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.workspace_name]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace Name"
|
|
||||||
default = "{{ workspace_name | default('default') }}"
|
|
||||||
help = "Name for this workspace (lowercase, alphanumeric and hyphens)"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
[items.workspace_description]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace Description"
|
|
||||||
default = "{{ workspace_description | default('') }}"
|
|
||||||
help = "Brief description of what this workspace manages"
|
|
||||||
|
|
||||||
[items.workspace_path]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace Directory Path"
|
|
||||||
default = "{{ workspace_path | default(home_dir + '/workspaces/default') }}"
|
|
||||||
help = "Where workspace files and configurations will be stored"
|
|
||||||
required = true
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# INFRASTRUCTURE DEFAULTS
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.infra_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Infrastructure Configuration"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.default_provider]
|
|
||||||
type = "select"
|
|
||||||
prompt = "Default Infrastructure Provider"
|
|
||||||
options = ["upcloud", "aws", "hetzner", "local"]
|
|
||||||
default = "{{ default_provider | default('upcloud') }}"
|
|
||||||
help = "Default cloud provider for servers created in this workspace"
|
|
||||||
|
|
||||||
[items.default_region]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Default Region/Zone"
|
|
||||||
default = "{{ default_region | default('') }}"
|
|
||||||
help = "Default deployment region (e.g., us-nyc1, eu-de-fra1, none for local)"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# INITIALIZATION OPTIONS
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.init_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Initialization Options"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.init_git]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Initialize Git Repository?"
|
|
||||||
help = "Create git repository for infrastructure as code version control"
|
|
||||||
|
|
||||||
[items.create_example_configs]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Create Example Configuration Files?"
|
|
||||||
help = "Generate sample server and infrastructure config files"
|
|
||||||
|
|
||||||
[items.setup_secrets]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Setup Secrets Management?"
|
|
||||||
help = "Configure KMS encryption and secrets storage"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# WORKSPACE FEATURES
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.features_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Workspace Features"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.enable_testing]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Enable Test Environment Service?"
|
|
||||||
help = "Enable Docker-based test environments for validating configurations"
|
|
||||||
|
|
||||||
[items.enable_monitoring]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Setup Monitoring?"
|
|
||||||
help = "Configure monitoring and observability for your infrastructure"
|
|
||||||
|
|
||||||
[items.enable_orchestrator]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Start Orchestrator Service?"
|
|
||||||
help = "Enable the orchestrator for workflow management and automation"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CONFIRMATION
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
[items.confirm_header]
|
|
||||||
type = "text"
|
|
||||||
prompt = "Review and Confirm"
|
|
||||||
display_only = true
|
|
||||||
|
|
||||||
[items.confirm_creation]
|
|
||||||
type = "confirm"
|
|
||||||
prompt = "Create workspace with these settings?"
|
|
||||||
help = "This will initialize the workspace directory and apply configurations"
|
|
||||||
required = true
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# FormInquire wrapper for shell scripts
|
|
||||||
# Simple wrapper to execute FormInquire forms from bash/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
FORM_FILE="${1:-}"
|
|
||||||
OUTPUT_FORMAT="${2:-json}"
|
|
||||||
|
|
||||||
# Check if form file provided
|
|
||||||
if [ -z "$FORM_FILE" ]; then
|
|
||||||
echo "Error: Form file required" >&2
|
|
||||||
echo "Usage: form.sh <form_file> [output_format]" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if form file exists
|
|
||||||
if [ ! -f "$FORM_FILE" ]; then
|
|
||||||
echo "Error: Form file not found: $FORM_FILE" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if forminquire is available
|
|
||||||
if ! command -v forminquire &> /dev/null; then
|
|
||||||
echo "Error: forminquire not found in PATH" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Execute forminquire
|
|
||||||
forminquire --from-file "$FORM_FILE" --output "$OUTPUT_FORMAT"
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
# Complete installation and registration of provisioning plugins
|
|
||||||
# Run this in a fresh Nushell session
|
|
||||||
|
|
||||||
print "Provisioning Plugins - Installation & Registration"
|
|
||||||
print "=================================================="
|
|
||||||
print ""
|
|
||||||
|
|
||||||
# Copy plugins to Nushell plugin directory
|
|
||||||
print "Step 1: Installing plugin binaries..."
|
|
||||||
print ""
|
|
||||||
|
|
||||||
let plugin_dir = ($env.HOME + "/.local/share/nushell/plugins")
|
|
||||||
|
|
||||||
# Run the registration script
|
|
||||||
let nu_path = ($env.HOME + "/.local/bin/nu" | path expand)
|
|
||||||
let register_script = ($env.PWD | path join "provisioning" "core" "plugins" "register-plugins.nu")
|
|
||||||
^$nu_path $register_script
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print "Step 2: Registering plugins with Nushell..."
|
|
||||||
print ""
|
|
||||||
|
|
||||||
# Register plugins
|
|
||||||
let auth_plugin = ($env.HOME | path join ".local/share/nushell/plugins/nu_plugin_auth" | path expand)
|
|
||||||
let kms_plugin = ($env.HOME | path join ".local/share/nushell/plugins/nu_plugin_kms" | path expand)
|
|
||||||
let orch_plugin = ($env.HOME | path join ".local/share/nushell/plugins/nu_plugin_orchestrator" | path expand)
|
|
||||||
|
|
||||||
plugin add $auth_plugin
|
|
||||||
plugin add $kms_plugin
|
|
||||||
plugin add $orch_plugin
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print "Step 3: Verifying plugin installation..."
|
|
||||||
print ""
|
|
||||||
|
|
||||||
# Verify plugins are loaded
|
|
||||||
let plugins = plugin list | where name =~ "nu_plugin_(auth|kms|orchestrator)"
|
|
||||||
|
|
||||||
if ($plugins | length) == 3 {
|
|
||||||
print "✓ All 3 plugins registered successfully!"
|
|
||||||
print ""
|
|
||||||
print "Installed plugins:"
|
|
||||||
for plugin in $plugins {
|
|
||||||
print $" ✓ ($plugin.name)"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print $"⚠ Expected 3 plugins, found ($plugins | length)"
|
|
||||||
print "Please run the following commands manually:"
|
|
||||||
print ""
|
|
||||||
print $"plugin add ($auth_plugin)"
|
|
||||||
print $"plugin add ($kms_plugin)"
|
|
||||||
print $"plugin add ($orch_plugin)"
|
|
||||||
}
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print "✓ Installation complete!"
|
|
||||||
print ""
|
|
||||||
print "You can now use the provisioning CLI with plugin support:"
|
|
||||||
print ""
|
|
||||||
print " provisioning auth login <username>"
|
|
||||||
print " provisioning kms encrypt <data>"
|
|
||||||
print " provisioning orch status"
|
|
||||||
@ -1,321 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
# Install and register provisioning critical plugins
|
|
||||||
#
|
|
||||||
# This is the main user-facing script for installing the three critical
|
|
||||||
# provisioning plugins (auth, kms, orchestrator) that provide:
|
|
||||||
# - 10-50x performance improvement over HTTP API
|
|
||||||
# - OS-native keyring integration
|
|
||||||
# - Local file-based operations (no network required)
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# nu install-plugins.nu # Build and install all plugins
|
|
||||||
# nu install-plugins.nu --skip-build # Register pre-built plugins only
|
|
||||||
# nu install-plugins.nu --release # Build release mode (default)
|
|
||||||
# nu install-plugins.nu --debug # Build debug mode
|
|
||||||
# nu install-plugins.nu --plugin auth # Install specific plugin only
|
|
||||||
# nu install-plugins.nu --verify # Verify after installation
|
|
||||||
|
|
||||||
const PLUGIN_DIR = "nushell-plugins"
|
|
||||||
|
|
||||||
const PROVISIONING_PLUGINS = [
|
|
||||||
"nu_plugin_auth"
|
|
||||||
"nu_plugin_kms"
|
|
||||||
"nu_plugin_orchestrator"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Build a single plugin
|
|
||||||
def build-plugin [
|
|
||||||
plugin_name: string
|
|
||||||
base_dir: path
|
|
||||||
--release
|
|
||||||
]: nothing -> record {
|
|
||||||
let plugin_path = ($base_dir | path join $PLUGIN_DIR $plugin_name)
|
|
||||||
|
|
||||||
if not ($plugin_path | path exists) {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "not_found"
|
|
||||||
message: $"Plugin directory not found: ($plugin_path)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_mode = if $release { "--release" } else { "" }
|
|
||||||
let target_dir = if $release { "release" } else { "debug" }
|
|
||||||
|
|
||||||
print $" Building ($plugin_name) \(($target_dir) mode\)..."
|
|
||||||
|
|
||||||
let start_time = (date now)
|
|
||||||
|
|
||||||
# Build the plugin
|
|
||||||
try {
|
|
||||||
cd $plugin_path
|
|
||||||
if $release {
|
|
||||||
cargo build --release
|
|
||||||
} else {
|
|
||||||
cargo build
|
|
||||||
}
|
|
||||||
cd -
|
|
||||||
|
|
||||||
let duration = ((date now) - $start_time) | into int | $in / 1_000_000_000
|
|
||||||
let binary_path = ($plugin_path | path join "target" $target_dir $plugin_name)
|
|
||||||
|
|
||||||
if ($binary_path | path exists) {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "built"
|
|
||||||
message: $"Build successful \(($duration | math round --precision 1)s\)"
|
|
||||||
path: $binary_path
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "error"
|
|
||||||
message: "Build completed but binary not found"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch { |err|
|
|
||||||
cd -
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "error"
|
|
||||||
message: $"Build failed: ($err.msg)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Register a plugin with Nushell
|
|
||||||
def register-plugin-binary [
|
|
||||||
plugin_name: string
|
|
||||||
binary_path: path
|
|
||||||
]: nothing -> record {
|
|
||||||
if not ($binary_path | path exists) {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "not_found"
|
|
||||||
message: $"Binary not found: ($binary_path)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
plugin add $binary_path
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "registered"
|
|
||||||
message: "Registered successfully"
|
|
||||||
path: $binary_path
|
|
||||||
}
|
|
||||||
} catch { |err|
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
status: "error"
|
|
||||||
message: $"Registration failed: ($err.msg)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get binary path for a plugin
|
|
||||||
def get-binary-path [
|
|
||||||
plugin_name: string
|
|
||||||
base_dir: path
|
|
||||||
--release
|
|
||||||
]: nothing -> path {
|
|
||||||
let target_dir = if ($release) { "release" } else { "debug" }
|
|
||||||
$base_dir | path join $PLUGIN_DIR $plugin_name "target" $target_dir $plugin_name
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print banner
|
|
||||||
def print-banner [] {
|
|
||||||
print ""
|
|
||||||
print "+======================================================+"
|
|
||||||
print "| Provisioning Platform - Plugin Installation |"
|
|
||||||
print "+======================================================+"
|
|
||||||
print ""
|
|
||||||
print "Installing critical plugins for optimal performance:"
|
|
||||||
print " - nu_plugin_auth: JWT auth with keyring (10x faster)"
|
|
||||||
print " - nu_plugin_kms: Multi-backend encryption (10x faster)"
|
|
||||||
print " - nu_plugin_orchestrator: Local operations (30x faster)"
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print summary
|
|
||||||
def print-summary [results: list] {
|
|
||||||
let built = ($results | where status == "built" | length)
|
|
||||||
let registered = ($results | where status == "registered" | length)
|
|
||||||
let errors = ($results | where status == "error" | length)
|
|
||||||
let skipped = ($results | where status in ["not_found" "already_registered"] | length)
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print "+------------------------------------------------------+"
|
|
||||||
print "| Installation Summary |"
|
|
||||||
print "+------------------------------------------------------+"
|
|
||||||
print ""
|
|
||||||
print $" Built: ($built)"
|
|
||||||
print $" Registered: ($registered)"
|
|
||||||
print $" Errors: ($errors)"
|
|
||||||
print $" Skipped: ($skipped)"
|
|
||||||
print ""
|
|
||||||
|
|
||||||
if $errors > 0 {
|
|
||||||
print "Some plugins failed to install. Check errors above."
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main entry point
|
|
||||||
def main [
|
|
||||||
--skip-build (-s) # Skip building, only register pre-built plugins
|
|
||||||
--release (-r) # Build in release mode (default)
|
|
||||||
--debug (-d) # Build in debug mode
|
|
||||||
--plugin (-p): string # Install specific plugin only
|
|
||||||
--verify (-v) # Verify installation after completion
|
|
||||||
--quiet (-q) # Suppress output
|
|
||||||
]: nothing -> nothing {
|
|
||||||
let base_dir = ($env.PWD | path dirname) # Go up from plugins/ to core/
|
|
||||||
let use_release = not $debug
|
|
||||||
|
|
||||||
# Determine which plugins to install
|
|
||||||
let plugins_to_install = if ($plugin != null) {
|
|
||||||
if $plugin in $PROVISIONING_PLUGINS {
|
|
||||||
[$plugin]
|
|
||||||
} else if $"nu_plugin_($plugin)" in $PROVISIONING_PLUGINS {
|
|
||||||
[$"nu_plugin_($plugin)"]
|
|
||||||
} else {
|
|
||||||
print $"Error: Unknown plugin '($plugin)'"
|
|
||||||
print $"Available: ($PROVISIONING_PLUGINS | str join ', ')"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$PROVISIONING_PLUGINS
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print-banner
|
|
||||||
}
|
|
||||||
|
|
||||||
mut all_results = []
|
|
||||||
|
|
||||||
# Phase 1: Build (unless --skip-build)
|
|
||||||
if not $skip_build {
|
|
||||||
if not $quiet {
|
|
||||||
let mode = if $use_release { "release" } else { "debug" }
|
|
||||||
print $"Phase 1: Building plugins \(($mode) mode\)..."
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for plugin_name in $plugins_to_install {
|
|
||||||
let result = (build-plugin $plugin_name $base_dir --release=$use_release)
|
|
||||||
$all_results = ($all_results | append $result)
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
match $result.status {
|
|
||||||
"built" => {
|
|
||||||
print $" [OK] ($result.message)"
|
|
||||||
}
|
|
||||||
"not_found" => {
|
|
||||||
print $" [SKIP] ($result.message)"
|
|
||||||
}
|
|
||||||
"error" => {
|
|
||||||
print $" [ERROR] ($result.message)"
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Phase 2: Register
|
|
||||||
if not $quiet {
|
|
||||||
print "Phase 2: Registering plugins with Nushell..."
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for plugin_name in $plugins_to_install {
|
|
||||||
let binary_path = (get-binary-path $plugin_name $base_dir --release=$use_release)
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print $" Registering ($plugin_name)..."
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = (register-plugin-binary $plugin_name $binary_path)
|
|
||||||
$all_results = ($all_results | append $result)
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
match $result.status {
|
|
||||||
"registered" => {
|
|
||||||
print $" [OK] ($result.message)"
|
|
||||||
}
|
|
||||||
"not_found" => {
|
|
||||||
print $" [SKIP] ($result.message)"
|
|
||||||
}
|
|
||||||
"error" => {
|
|
||||||
print $" [ERROR] ($result.message)"
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print-summary $all_results
|
|
||||||
}
|
|
||||||
|
|
||||||
# Phase 3: Verify (if requested)
|
|
||||||
if $verify {
|
|
||||||
if not $quiet {
|
|
||||||
print "Phase 3: Verifying installation..."
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
let registered_plugins = (plugin list)
|
|
||||||
|
|
||||||
for plugin_name in $plugins_to_install {
|
|
||||||
let is_registered = ($registered_plugins | where name == $plugin_name | length) > 0
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
if $is_registered {
|
|
||||||
print $" [OK] ($plugin_name) is registered"
|
|
||||||
} else {
|
|
||||||
print $" [FAIL] ($plugin_name) not found in plugin list"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $quiet {
|
|
||||||
print "Plugin commands now available:"
|
|
||||||
print ""
|
|
||||||
print " Authentication:"
|
|
||||||
print " auth login <user> [password] # Login with JWT"
|
|
||||||
print " auth logout # End session"
|
|
||||||
print " auth verify # Check token"
|
|
||||||
print " auth sessions # List sessions"
|
|
||||||
print " auth mfa enroll totp # Setup MFA"
|
|
||||||
print " auth mfa verify --code 123456 # Verify MFA"
|
|
||||||
print ""
|
|
||||||
print " Encryption (KMS):"
|
|
||||||
print " kms encrypt \"data\" --backend age # Encrypt with Age"
|
|
||||||
print " kms decrypt \$encrypted # Decrypt data"
|
|
||||||
print " kms generate-key --spec AES256 # Generate key"
|
|
||||||
print " kms status # Check KMS status"
|
|
||||||
print " kms list-backends # Available backends"
|
|
||||||
print ""
|
|
||||||
print " Orchestrator:"
|
|
||||||
print " orch status # Local status (fast)"
|
|
||||||
print " orch tasks # List tasks"
|
|
||||||
print " orch validate workflow.k # Validate KCL"
|
|
||||||
print " orch submit workflow.k # Submit workflow"
|
|
||||||
print " orch monitor <task_id> # Monitor task"
|
|
||||||
print ""
|
|
||||||
print "Verify installation with: plugin list"
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
# Register provisioning plugins with Nushell
|
|
||||||
|
|
||||||
# Detect plugin directory
|
|
||||||
let plugin_dir = ($env.HOME + "/.local/share/nushell/plugins")
|
|
||||||
|
|
||||||
print $"Using plugin directory: ($plugin_dir)"
|
|
||||||
|
|
||||||
# Create plugin directory if it doesn't exist
|
|
||||||
if not ($plugin_dir | path exists) {
|
|
||||||
mkdir $plugin_dir
|
|
||||||
}
|
|
||||||
|
|
||||||
# Define plugins to register
|
|
||||||
let plugins = [
|
|
||||||
{
|
|
||||||
name: "nu_plugin_auth"
|
|
||||||
src: "provisioning/core/plugins/nushell-plugins/nu_plugin_auth/target/release/nu_plugin_auth"
|
|
||||||
description: "JWT authentication with system keyring"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: "nu_plugin_kms"
|
|
||||||
src: "provisioning/core/plugins/nushell-plugins/nu_plugin_kms/target/release/nu_plugin_kms"
|
|
||||||
description: "Multi-backend KMS encryption"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: "nu_plugin_orchestrator"
|
|
||||||
src: "provisioning/core/plugins/nushell-plugins/nu_plugin_orchestrator/target/release/nu_plugin_orchestrator"
|
|
||||||
description: "Local orchestrator operations (30x faster)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Copy plugins
|
|
||||||
print ""
|
|
||||||
print "Installing plugins..."
|
|
||||||
print "===================="
|
|
||||||
|
|
||||||
let result = (
|
|
||||||
$plugins
|
|
||||||
| each { |plugin|
|
|
||||||
let src = $plugin.src
|
|
||||||
let dst = ($plugin_dir + "/" + $plugin.name)
|
|
||||||
|
|
||||||
if not ($src | path exists) {
|
|
||||||
{
|
|
||||||
name: $plugin.name
|
|
||||||
success: false
|
|
||||||
message: $"Source not found at ($src)"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cp $src $dst
|
|
||||||
chmod +x $dst
|
|
||||||
if ($dst | path exists) {
|
|
||||||
{
|
|
||||||
name: $plugin.name
|
|
||||||
success: true
|
|
||||||
message: $"Installed to ($dst)"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
{
|
|
||||||
name: $plugin.name
|
|
||||||
success: false
|
|
||||||
message: "Failed to copy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Print results
|
|
||||||
for item in $result {
|
|
||||||
let icon = if $item.success { "✓" } else { "✗" }
|
|
||||||
print $"($icon) ($item.name): ($item.message)"
|
|
||||||
}
|
|
||||||
|
|
||||||
let installed = ($result | where success == true | length)
|
|
||||||
let failed = ($result | where success == false | length)
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print $"Results: ($installed) installed, ($failed) failed"
|
|
||||||
|
|
||||||
# Suggest next steps
|
|
||||||
if $installed > 0 {
|
|
||||||
print ""
|
|
||||||
print "Next steps:"
|
|
||||||
print "==========="
|
|
||||||
print "Run the following command in a new Nushell session:"
|
|
||||||
print ""
|
|
||||||
for plugin in $plugins {
|
|
||||||
let dst = ($plugin_dir + "/" + $plugin.name)
|
|
||||||
print $" plugin add ($dst)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,264 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
# Test provisioning plugins are installed and working
|
|
||||||
#
|
|
||||||
# This script verifies that the three critical provisioning plugins
|
|
||||||
# are properly installed, registered, and functional.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# nu test-plugins.nu # Run all tests
|
|
||||||
# nu test-plugins.nu --quick # Quick registration check only
|
|
||||||
# nu test-plugins.nu --verbose # Detailed output
|
|
||||||
# nu test-plugins.nu --json # Output as JSON
|
|
||||||
|
|
||||||
const PROVISIONING_PLUGINS = [
|
|
||||||
{
|
|
||||||
name: "nu_plugin_auth"
|
|
||||||
test_command: "auth verify --local"
|
|
||||||
expected_fields: ["valid"]
|
|
||||||
description: "JWT authentication with system keyring"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: "nu_plugin_kms"
|
|
||||||
test_command: "kms status"
|
|
||||||
expected_fields: ["backend", "available"]
|
|
||||||
description: "Multi-backend KMS encryption"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: "nu_plugin_orchestrator"
|
|
||||||
test_command: "orch status"
|
|
||||||
expected_fields: ["running", "tasks_pending"]
|
|
||||||
description: "Local orchestrator operations"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Check if plugin is registered
|
|
||||||
def check-registration [plugin_name: string]: nothing -> record {
|
|
||||||
let registered_plugins = (plugin list | get name?)
|
|
||||||
|
|
||||||
if ($registered_plugins == null) {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
registered: false
|
|
||||||
message: "Could not query plugin list"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_registered = $plugin_name in $registered_plugins
|
|
||||||
|
|
||||||
{
|
|
||||||
name: $plugin_name
|
|
||||||
registered: $is_registered
|
|
||||||
message: (if $is_registered { "Registered" } else { "Not registered" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test plugin functionality
|
|
||||||
def test-plugin-function [
|
|
||||||
plugin_name: string
|
|
||||||
test_command: string
|
|
||||||
expected_fields: list
|
|
||||||
]: nothing -> record {
|
|
||||||
let start_time = (date now)
|
|
||||||
|
|
||||||
try {
|
|
||||||
# Execute the test command
|
|
||||||
let result = (nu -c $test_command | from json)
|
|
||||||
let duration = ((date now) - $start_time) | into int | $in / 1_000_000
|
|
||||||
|
|
||||||
# Check if expected fields exist
|
|
||||||
let missing_fields = ($expected_fields | where { |field|
|
|
||||||
not ($field in ($result | columns))
|
|
||||||
})
|
|
||||||
|
|
||||||
if ($missing_fields | length) > 0 {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
functional: false
|
|
||||||
message: $"Missing fields: ($missing_fields | str join ', ')"
|
|
||||||
duration_ms: $duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
name: $plugin_name
|
|
||||||
functional: true
|
|
||||||
message: "Commands working"
|
|
||||||
duration_ms: $duration
|
|
||||||
result: $result
|
|
||||||
}
|
|
||||||
} catch { |err|
|
|
||||||
let duration = ((date now) - $start_time) | into int | $in / 1_000_000
|
|
||||||
|
|
||||||
# Some commands might return errors but still work (e.g., no token)
|
|
||||||
# This is expected behavior, not a failure
|
|
||||||
if ($err.msg | str contains "not logged in") or
|
|
||||||
($err.msg | str contains "token not found") or
|
|
||||||
($err.msg | str contains "No sessions") {
|
|
||||||
return {
|
|
||||||
name: $plugin_name
|
|
||||||
functional: true
|
|
||||||
message: "Commands working (expected auth state)"
|
|
||||||
duration_ms: $duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
name: $plugin_name
|
|
||||||
functional: false
|
|
||||||
message: $"Error: ($err.msg)"
|
|
||||||
duration_ms: $duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
def run-tests [
|
|
||||||
--quick: bool = false
|
|
||||||
--verbose: bool = false
|
|
||||||
]: nothing -> list {
|
|
||||||
mut results = []
|
|
||||||
|
|
||||||
for plugin in $PROVISIONING_PLUGINS {
|
|
||||||
# Registration check
|
|
||||||
let reg_result = (check-registration $plugin.name)
|
|
||||||
$results = ($results | append {
|
|
||||||
plugin: $plugin.name
|
|
||||||
test: "registration"
|
|
||||||
passed: $reg_result.registered
|
|
||||||
message: $reg_result.message
|
|
||||||
duration_ms: 0
|
|
||||||
})
|
|
||||||
|
|
||||||
if $verbose {
|
|
||||||
let status = if $reg_result.registered { "[PASS]" } else { "[FAIL]" }
|
|
||||||
print $"($status) ($plugin.name) - Registration: ($reg_result.message)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Skip functional tests if quick mode or not registered
|
|
||||||
if $quick or (not $reg_result.registered) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
# Functional test
|
|
||||||
let func_result = (test-plugin-function $plugin.name $plugin.test_command $plugin.expected_fields)
|
|
||||||
$results = ($results | append {
|
|
||||||
plugin: $plugin.name
|
|
||||||
test: "functional"
|
|
||||||
passed: $func_result.functional
|
|
||||||
message: $func_result.message
|
|
||||||
duration_ms: $func_result.duration_ms
|
|
||||||
})
|
|
||||||
|
|
||||||
if $verbose {
|
|
||||||
let status = if $func_result.functional { "[PASS]" } else { "[FAIL]" }
|
|
||||||
print $"($status) ($plugin.name) - Functional: ($func_result.message) \(($func_result.duration_ms)ms\)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$results
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main entry point
|
|
||||||
def main [
|
|
||||||
--quick (-q) # Quick registration check only
|
|
||||||
--verbose (-v) # Detailed output
|
|
||||||
--json (-j) # Output as JSON
|
|
||||||
]: nothing -> nothing {
|
|
||||||
if not $json {
|
|
||||||
print ""
|
|
||||||
print "======================================================"
|
|
||||||
print " Provisioning Plugins Test Suite"
|
|
||||||
print "======================================================"
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = (run-tests --quick=$quick --verbose=$verbose)
|
|
||||||
|
|
||||||
# Calculate summary
|
|
||||||
let total_tests = ($results | length)
|
|
||||||
let passed_tests = ($results | where passed == true | length)
|
|
||||||
let failed_tests = ($results | where passed == false | length)
|
|
||||||
|
|
||||||
# Registration summary
|
|
||||||
let reg_results = ($results | where test == "registration")
|
|
||||||
let reg_passed = ($reg_results | where passed == true | length)
|
|
||||||
let reg_total = ($reg_results | length)
|
|
||||||
|
|
||||||
# Functional summary
|
|
||||||
let func_results = ($results | where test == "functional")
|
|
||||||
let func_passed = ($func_results | where passed == true | length)
|
|
||||||
let func_total = ($func_results | length)
|
|
||||||
|
|
||||||
if $json {
|
|
||||||
# JSON output
|
|
||||||
{
|
|
||||||
total: $total_tests
|
|
||||||
passed: $passed_tests
|
|
||||||
failed: $failed_tests
|
|
||||||
registration: {
|
|
||||||
total: $reg_total
|
|
||||||
passed: $reg_passed
|
|
||||||
}
|
|
||||||
functional: {
|
|
||||||
total: $func_total
|
|
||||||
passed: $func_passed
|
|
||||||
}
|
|
||||||
results: $results
|
|
||||||
all_passed: ($failed_tests == 0)
|
|
||||||
} | to json
|
|
||||||
} else {
|
|
||||||
# Human-readable output
|
|
||||||
if not $verbose {
|
|
||||||
# Print results table
|
|
||||||
print "Test Results:"
|
|
||||||
print ""
|
|
||||||
|
|
||||||
for result in $results {
|
|
||||||
let status = if $result.passed { "[OK] " } else { "[FAIL]" }
|
|
||||||
let test_type = if $result.test == "registration" { "reg" } else { "func" }
|
|
||||||
let duration = if $result.duration_ms > 0 {
|
|
||||||
$" \(($result.duration_ms)ms\)"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
print $" ($status) ($result.plugin | fill -w 25) ($test_type | fill -w 5) ($result.message)($duration)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print ""
|
|
||||||
print "------------------------------------------------------"
|
|
||||||
print "Summary:"
|
|
||||||
print $" Registration: ($reg_passed)/($reg_total) passed"
|
|
||||||
|
|
||||||
if not $quick {
|
|
||||||
print $" Functional: ($func_passed)/($func_total) passed"
|
|
||||||
}
|
|
||||||
|
|
||||||
print $" Total: ($passed_tests)/($total_tests) passed"
|
|
||||||
print "------------------------------------------------------"
|
|
||||||
print ""
|
|
||||||
|
|
||||||
if $failed_tests == 0 {
|
|
||||||
print "All tests passed! Plugins are ready to use."
|
|
||||||
} else {
|
|
||||||
print $"($failed_tests) test\(s\) failed. Some plugins may need attention."
|
|
||||||
print ""
|
|
||||||
print "Troubleshooting:"
|
|
||||||
print " 1. Build plugins: nu install-plugins.nu"
|
|
||||||
print " 2. Register only: nu install-plugins.nu --skip-build"
|
|
||||||
print " 3. Check plugin list: plugin list"
|
|
||||||
}
|
|
||||||
|
|
||||||
print ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# Exit with error code if tests failed
|
|
||||||
if $failed_tests > 0 {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Export for module usage
|
|
||||||
export def "provisioning-plugins test" [--quick (-q), --verbose (-v), --json (-j)] {
|
|
||||||
main --quick=$quick --verbose=$verbose --json=$json
|
|
||||||
}
|
|
||||||
101
versions.k
101
versions.k
@ -1,101 +0,0 @@
|
|||||||
import provisioning.version as prv_schema
|
|
||||||
|
|
||||||
# Core tools versions for provisioning system as array
|
|
||||||
# Converted from individual declarations to array of TaskservVersion items
|
|
||||||
core_versions: [prv_schema.TaskservVersion] = [
|
|
||||||
prv_schema.TaskservVersion {
|
|
||||||
name = "nushell"
|
|
||||||
version = prv_schema.Version {
|
|
||||||
current = "0.109.1"
|
|
||||||
source = "https://github.com/nushell/nushell/releases"
|
|
||||||
tags = "https://github.com/nushell/nushell/tags"
|
|
||||||
site = "https://www.nushell.sh/"
|
|
||||||
# Pinned for system stability
|
|
||||||
check_latest = False
|
|
||||||
grace_period = 86400
|
|
||||||
}
|
|
||||||
dependencies = []
|
|
||||||
detector = {
|
|
||||||
method = "command"
|
|
||||||
command = "nu -v"
|
|
||||||
pattern = r"(?P<capture0>[\d.]+\.[\d.]+)"
|
|
||||||
capture = "capture0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prv_schema.TaskservVersion {
|
|
||||||
name = "kcl"
|
|
||||||
version = prv_schema.Version {
|
|
||||||
current = "0.11.3"
|
|
||||||
source = "https://github.com/kcl-lang/cli/releases"
|
|
||||||
tags = "https://github.com/kcl-lang/cli/tags"
|
|
||||||
site = "https://kcl-lang.io"
|
|
||||||
# Pinned for system stability
|
|
||||||
check_latest = False
|
|
||||||
grace_period = 86400
|
|
||||||
}
|
|
||||||
dependencies = []
|
|
||||||
detector = {
|
|
||||||
method = "command"
|
|
||||||
command = "kcl -v"
|
|
||||||
pattern = r"kcl\s+version\s+(?P<capture0>[\d.]+)"
|
|
||||||
capture = "capture0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prv_schema.TaskservVersion {
|
|
||||||
name = "sops"
|
|
||||||
version = prv_schema.Version {
|
|
||||||
current = "3.10.2"
|
|
||||||
source = "https://github.com/getsops/sops/releases"
|
|
||||||
tags = "https://github.com/getsops/sops/tags"
|
|
||||||
site = "https://github.com/getsops/sops"
|
|
||||||
# Pinned for encryption compatibility
|
|
||||||
check_latest = False
|
|
||||||
grace_period = 86400
|
|
||||||
}
|
|
||||||
dependencies = ["age"]
|
|
||||||
detector = {
|
|
||||||
method = "command"
|
|
||||||
command = "sops -v"
|
|
||||||
pattern = r"sops\s+(?P<capture0>[\d.]+)"
|
|
||||||
capture = "capture0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prv_schema.TaskservVersion {
|
|
||||||
name = "age"
|
|
||||||
version = prv_schema.Version {
|
|
||||||
current = "1.2.1"
|
|
||||||
source = "https://github.com/FiloSottile/age/releases"
|
|
||||||
tags = "https://github.com/FiloSottile/age/tags"
|
|
||||||
site = "https://github.com/FiloSottile/age"
|
|
||||||
# Pinned for encryption compatibility
|
|
||||||
check_latest = False
|
|
||||||
grace_period = 86400
|
|
||||||
}
|
|
||||||
dependencies = []
|
|
||||||
detector = {
|
|
||||||
method = "command"
|
|
||||||
command = "age --version"
|
|
||||||
pattern = r"v(?P<capture0>[\d.]+)"
|
|
||||||
capture = "capture0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prv_schema.TaskservVersion {
|
|
||||||
name = "k9s"
|
|
||||||
version = prv_schema.Version {
|
|
||||||
current = "0.50.6"
|
|
||||||
source = "https://github.com/derailed/k9s/releases"
|
|
||||||
tags = "https://github.com/derailed/k9s/tags"
|
|
||||||
site = "https://k9scli.io/"
|
|
||||||
# Can auto-update for CLI tools
|
|
||||||
check_latest = True
|
|
||||||
grace_period = 86400
|
|
||||||
}
|
|
||||||
dependencies = []
|
|
||||||
detector = {
|
|
||||||
method = "command"
|
|
||||||
command = "k9s version"
|
|
||||||
pattern = r"Version\s+v(?P<capture0>[\d.]+)"
|
|
||||||
capture = "capture0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Loading…
x
Reference in New Issue
Block a user