provisioning-core/nulib/domain/workspace/generate_docs.nu

219 lines
6.6 KiB
Text

# Workspace Documentation Generator
# Generates deployment, configuration, and troubleshooting guides from Jinja2 templates
# Uses workspace metadata to populate guide variables
use tools/nickel/process.nu [ncl-eval]
def extract-workspace-metadata [workspace_path: string] {
{
workspace_path: $workspace_path,
config_path: $"($workspace_path)/config/config.ncl",
}
}
def extract-workspace-name [metadata: record] {
ncl-eval ($metadata.workspace_path | path join "config/config.ncl") [$metadata.workspace_path] | get workspace.name
}
def extract-provider-config [metadata: record] {
let config = (ncl-eval ($metadata.workspace_path | path join "config/config.ncl") [$metadata.workspace_path])
let providers = $config.providers
let provider_names = ($providers | columns)
let provider_list = (
$provider_names
| each { |name| { name: $name, enabled: (($providers | get $name).enabled) } }
)
let first_enabled_provider = (
$provider_list
| where enabled == true
| first
| get name
)
{
name: $first_enabled_provider,
enabled: true
}
}
def extract-infrastructures [workspace_path: string] {
let infra_dir = $"($workspace_path)/infra"
if ($infra_dir | path exists) {
ls $infra_dir
| where type == dir
| get name
| each { |path| $path | path basename }
} else {
[]
}
}
def extract-servers [workspace_path: string, infra: string] {
let servers_file = $"($workspace_path)/infra/($infra)/servers.ncl"
if ($servers_file | path exists) {
let exported = (ncl-eval $servers_file [$workspace_path])
$exported.servers
} else {
[]
}
}
def extract-taskservs [workspace_path: string, infra: string] {
let taskservs_dir = $"($workspace_path)/infra/($infra)/taskservs"
if ($taskservs_dir | path exists) {
ls $taskservs_dir
| where name ends-with .ncl
| get name
| each { |path| $path | path basename | str replace --regex '\.ncl$' '' }
} else {
[]
}
}
def generate-guide [template_path: string, output_path: string, variables: record] {
let output_dir = ($output_path | path dirname)
if not ($output_dir | path exists) {
mkdir $output_dir
}
let rendered = (tera-render $template_path $variables)
$rendered | save --force $output_path
}
export def generate-all-guides [workspace_path: string, template_dir: string, output_dir: string] {
let metadata = (extract-workspace-metadata $workspace_path)
let workspace_name = (extract-workspace-name $metadata)
let provider_info = (extract-provider-config $metadata)
let all_infra = (extract-infrastructures $workspace_path)
# Filter out library/technical directories
let infrastructures = ($all_infra | where $it != "lib")
let default_infra = if ($infrastructures | is-empty) {
"default"
} else {
$infrastructures | first
}
let extracted_servers = (extract-servers $workspace_path $default_infra)
let taskservs = (extract-taskservs $workspace_path $default_infra)
# Map server fields to template-friendly names
let servers = (
$extracted_servers
| each { |srv|
let stg = if (($srv.storages | length) > 0) {
($srv.storages | get 0).total
} else {
0
}
{ name: $srv.hostname, plan: $srv.plan, storage: $stg, provider: $srv.provider, zone: $srv.zone }
}
)
let variables = {
workspace_name: $workspace_name,
workspace_path: $workspace_path,
workspace_description: "Workspace infrastructure deployment",
primary_provider: $provider_info.name,
primary_zone: "es-mad1",
alternate_zone: "nl-ams1",
default_infra: $default_infra,
providers: [$provider_info.name],
infrastructures: $infrastructures,
servers: $servers,
taskservs: $taskservs,
pricing_estimate: "€30-40/month",
provider_url: "https://hub.upcloud.com",
provider_api_url: "https://upcloud.com/api/",
provider_api_host: "api.upcloud.com",
provider_status_url: "https://status.upcloud.com",
provider_env_vars: {
"UPCLOUD_USER": "username",
"UPCLOUD_PASSWORD": "password",
},
provider_defaults: {
"api_timeout": "30",
},
provider_zone_defaults: {
"zone": "es-mad1",
"plan": "2xCPU-4GB",
},
infrastructure_purposes: {
"wuji": "Kubernetes cluster for production workloads",
"sgoyol": "Development and testing environment",
},
server_plans: [
"1xCPU-1GB",
"1xCPU-2GB",
"2xCPU-4GB",
"2xCPU-8GB",
"4xCPU-8GB",
"4xCPU-16GB",
],
available_zones: [
"us-east-1",
"us-west-1",
"nl-ams1",
"es-mad1",
"fi-hel1",
],
provider_config_example: {
"username": "your-username",
"password": "your-password",
"default-zone": "es-mad1",
},
}
print $"Generating guides for workspace: ($workspace_name)"
let guides = [
{
template: "deployment-guide.md.j2",
output: "deployment-guide.md",
},
{
template: "configuration-guide.md.j2",
output: "configuration-guide.md",
},
{
template: "troubleshooting.md.j2",
output: "troubleshooting.md",
},
{
template: "README.md.j2",
output: "README.md",
},
]
$guides
| each { |guide|
let template_path = $"($template_dir)/($guide.template)"
let output_path = $"($output_dir)/($guide.output)"
print $" Generating ($guide.output)..."
generate-guide $template_path $output_path $variables
}
print "Documentation generation complete!"
}
def main [workspace_path: string] {
# Get absolute paths - resolve from project root
let current_dir = (pwd)
let abs_workspace_path = (($workspace_path | path expand) | if (($in | path type) == relative) { ($"($current_dir)/($in)") } else { $in })
let template_dir = ($"($current_dir)/provisioning/templates/docs" | path expand)
let output_dir = ($"($abs_workspace_path)/docs" | path expand)
if not ($template_dir | path exists) {
print $"Template directory not found at ($template_dir)"
} else {
generate-all-guides $abs_workspace_path $template_dir $output_dir
}
}