220 lines
6.6 KiB
Text
220 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
|
||
|
|
}
|
||
|
|
}
|