From 40a95cc4a6c0afd3e8feb1b3a7b65b68e26e7760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 9 Aug 2026 21:56:40 +0400 Subject: [PATCH] =?UTF-8?q?feat(P2+P3):=20docs-generator=20=E2=80=94=20?= =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20ID,=20value=5Flist=20?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D0=BC=D1=8B=D0=B9,=20=D0=B4?= =?UTF-8?q?=D0=B2=D0=BE=D0=B9=D0=BD=D0=BE=D0=B9=20=D0=BF=D1=80=D0=B8=D0=BC?= =?UTF-8?q?=D0=B5=D1=80,=20=5Fnav=5Ffragment.yml,=20mkdocs=20breadcrumbs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderParamTable/renderModifyTable/renderNestedParams: убрать колонку ID - collectConstraints: value_list → Допустимые значения - buildExamplePage: минимальный пример + полный в
- minimalExampleBlock: только required без default - WriteNavFragment: генерация _nav_fragment.yml с категориями - mkdocs.yml: navigation.path, navigation.footer, navigation.indexes --- .../internal/writers/writers.go | 149 +++++++++++++++--- TOOLS/docs-generator/main.go | 1 + mkdocs.yml | 5 +- 3 files changed, 129 insertions(+), 26 deletions(-) diff --git a/TOOLS/docs-generator/internal/writers/writers.go b/TOOLS/docs-generator/internal/writers/writers.go index f4c52a7..4e8785e 100644 --- a/TOOLS/docs-generator/internal/writers/writers.go +++ b/TOOLS/docs-generator/internal/writers/writers.go @@ -46,6 +46,21 @@ func ResourceDocs(docsDir string, spec types.ServiceSpec, version string, apiEnd } } +// CollectSubresources возвращает уникальные subresource-имена. +func CollectSubresources(spec types.ServiceSpec) []string { + seen := map[string]bool{} + var out []string + for _, op := range spec.Operations { + if op.Kind == "subresource" && op.Subresource != "" { + if !seen[op.Subresource] { + seen[op.Subresource] = true + out = append(out, op.Subresource) + } + } + } + return out +} + // WriteFile пишет контент в файл. func WriteFile(path, content string) { if err := os.WriteFile(path, []byte(content), 0o644); err != nil { @@ -72,21 +87,6 @@ func IndexMD(docsDir string, specs []types.ServiceSpec) { } } -// CollectSubresources возвращает уникальные subresource-имена. -func CollectSubresources(spec types.ServiceSpec) []string { - seen := map[string]bool{} - var out []string - for _, op := range spec.Operations { - if op.Kind == "subresource" && op.Subresource != "" { - if !seen[op.Subresource] { - seen[op.Subresource] = true - out = append(out, op.Subresource) - } - } - } - return out -} - // FindParams находит параметры операции по action. func FindParams(ops []types.OperationSpec, action string) []types.ParamSpec { for _, op := range ops { @@ -187,10 +187,15 @@ func htmlToMarkdown(s string) string { func buildExamplePage(spec types.ServiceSpec, nav, version string, apiEndpoint string, providerSource string) string { var b strings.Builder b.WriteString(buildHeader(spec, nav)) - b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_main.tf`)\n\n", spec.Name)) + b.WriteString(fmt.Sprintf("## Minimal example — only required parameters\n\n")) + b.WriteString("```hcl\n") + b.WriteString(minimalExampleBlock(spec, version, apiEndpoint, providerSource)) + b.WriteString("```\n\n") + b.WriteString("
Full example (all parameters, including defaults)\n\n") b.WriteString("```hcl\n") b.WriteString(exampleBlock(spec, version, apiEndpoint, providerSource)) b.WriteString("```\n\n") + b.WriteString("
\n\n") b.WriteString("## Outputs usage\n\n") b.WriteString("```hcl\n") b.WriteString("# Выходные параметры можно использовать как\n") @@ -242,6 +247,40 @@ func exampleBlock(spec types.ServiceSpec, version string, apiEndpoint string, pr return b.String() } +// minimalExampleBlock — только required параметры без default (минимальный работающий пример). +func minimalExampleBlock(spec types.ServiceSpec, version string, apiEndpoint string, providerSource string) string { + createParams := FindParams(spec.Operations, "create") + var minimalParams []types.ParamSpec + for _, p := range createParams { + if p.Required && !hasDefault(p.Default) { + minimalParams = append(minimalParams, p) + } + } + + var b strings.Builder + b.WriteString("terraform {\n") + b.WriteString(" required_providers {\n") + b.WriteString(" nubes = {\n") + b.WriteString(fmt.Sprintf(" source = \"%s\"\n", providerSource)) + b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version)) + b.WriteString(" }\n") + b.WriteString(" }\n") + b.WriteString("}\n\n") + b.WriteString("provider \"nubes\" {\n") + b.WriteString(" api_token = \"***\"\n") + b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint)) + b.WriteString("}\n") + b.WriteString(fmt.Sprintf("resource \"nubes_%s\" \"baza\" {\n", spec.Name)) + b.WriteString(" resource_name = \"my-resource\"\n\n") + + for _, p := range minimalParams { + b.WriteString(formatParamOrBlock(p, " ", true)) + } + + b.WriteString("}\n") + return b.String() +} + func buildCreateParamsPage(spec types.ServiceSpec, nav string) string { var b strings.Builder b.WriteString(buildHeader(spec, nav)) @@ -521,13 +560,12 @@ func renderParamTable(params []types.ParamSpec, requiredTable, noDefault bool) s } b.WriteString(fmt.Sprintf("\n", tableClass)) if noDefault { - b.WriteString("\n\n") + b.WriteString("\n\n") } else { - b.WriteString("\n\n") + b.WriteString("\n\n") } for _, p := range params { b.WriteString("") - b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) b.WriteString(fmt.Sprintf("", formatTypeCell(p))) if !noDefault { @@ -547,10 +585,9 @@ func renderModifyTable(params []types.ParamSpec) string { } var b strings.Builder b.WriteString("
IDCodeTypeDescriptionConstraints
CodeTypeDescriptionConstraints
IDCodeTypeDefaultDescriptionConstraints
CodeTypeDefaultDescriptionConstraints
%s%s%s
\n") - b.WriteString("\n\n") + b.WriteString("\n\n") for _, p := range params { b.WriteString("") - b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) b.WriteString(fmt.Sprintf("", formatTypeCell(p))) b.WriteString(fmt.Sprintf("", escapeText(pickTextTable(p)))) @@ -806,7 +843,7 @@ func collectConstraints(p types.ParamSpec) string { parts = append(parts, fmt.Sprintf("regex=%s", p.Regex)) } if len(p.ValueList) > 0 { - parts = append(parts, fmt.Sprintf("value_list=%s", strings.Join(p.ValueList, ", "))) + parts = append(parts, fmt.Sprintf("Допустимые значения: %s", strings.Join(p.ValueList, ", "))) } if p.Func != "" { parts = append(parts, fmt.Sprintf("func=%s", p.Func)) @@ -992,14 +1029,13 @@ func renderNestedParams(p types.ParamSpec) string { } b.WriteString(fmt.Sprintf("\n### %s\n\n", label)) b.WriteString("
IDCodeTypeDescriptionConstraints
CodeTypeDescriptionConstraints
%s%s%s%s
\n") - b.WriteString("\n\n") + b.WriteString("\n\n") for _, sp := range p.SubParams { req := "no" if sp.Required { req = "**yes**" } b.WriteString("") - b.WriteString(fmt.Sprintf("", escapeText(formatID(sp.ID)))) b.WriteString(fmt.Sprintf("", formatParamCode(sp.Code))) b.WriteString(fmt.Sprintf("", formatTypeCell(sp))) b.WriteString(fmt.Sprintf("", req)) @@ -1011,3 +1047,68 @@ func renderNestedParams(p types.ParamSpec) string { b.WriteString("
IDCodeTypeRequiredDefaultDescriptionConstraints
CodeTypeRequiredDefaultDescriptionConstraints
%s%s%s%s
\n") return b.String() } + +// serviceCategory возвращает категорию для сервиса. +func serviceCategory(name string) string { + cats := map[string]string{ + "postgres": "Базы данных", "redis": "Базы данных", "mongodb": "Базы данных", + "mariadb": "Базы данных", "clickhouse": "Базы данных", + "rabbitmq": "Очереди", "kafka": "Очереди", + "s3": "Хранилище", "s3bucket": "Хранилище", "nextcloud": "Хранилище", + "k8s_velero": "K8s", "k8s_sthutrval_cluster": "K8s", "k8s_openbao": "K8s", + "vc_mgmt_sthutrval_cluster": "K8s", + "vc_org": "VMware", "vc_vdc": "VMware", "vc_nsxt": "VMware", + "vcexternalip": "VMware", "vapp": "VMware", "vc_vm_v2": "VMware", + "vc_vm_v3": "VMware", "vc_vdc_group": "VMware", + "flask": "Приложения", "nodejs": "Приложения", "lucee": "Приложения", + "http": "Приложения", "gitea": "Приложения", "superset": "Приложения", + "pgadmin": "Приложения", "harbor": "Приложения", "akhq": "Приложения", + "llm_ai": "Приложения", "template": "Приложения", "dummy": "Приложения", + "valo_tenant": "Приложения", + "zones_v2": "Сеть", "dnsrecord": "Сеть", + } + if cat, ok := cats[name]; ok { + return cat + } + return "Другие" +} + +// WriteNavFragment генерирует _nav_fragment.yml для mkdocs sidebar. +func WriteNavFragment(docsDir string, specs []types.ServiceSpec) { + catMap := map[string][]types.ServiceSpec{} + for _, s := range specs { + cat := serviceCategory(s.Name) + catMap[cat] = append(catMap[cat], s) + } + + var b bytes.Buffer + b.WriteString("# auto-generated by docs-generator — DO NOT EDIT\n") + b.WriteString("resources_nav:\n") + + order := []string{"Базы данных", "Очереди", "Хранилище", "K8s", "VMware", "Приложения", "Сеть", "Другие"} + for _, cat := range order { + svcs := catMap[cat] + if len(svcs) == 0 { + continue + } + b.WriteString(fmt.Sprintf(" - %s:\n", cat)) + for _, s := range svcs { + disp := s.ServiceDisplayName + if disp == "" { + disp = s.Name + } + b.WriteString(fmt.Sprintf(" - %s:\n", disp)) + b.WriteString(fmt.Sprintf(" - Обзор: %s.md\n", s.Name)) + b.WriteString(fmt.Sprintf(" - Параметры создания: %s_params_create.md\n", s.Name)) + b.WriteString(fmt.Sprintf(" - Параметры изменения: %s_params_modify.md\n", s.Name)) + b.WriteString(fmt.Sprintf(" - Выходные данные: %s_outputs.md\n", s.Name)) + b.WriteString(fmt.Sprintf(" - Операции: %s_ops.md\n", s.Name)) + b.WriteString(fmt.Sprintf(" - Пример: %s_example.md\n", s.Name)) + } + } + + outPath := filepath.Join(docsDir, "_nav_fragment.yml") + if err := os.WriteFile(outPath, b.Bytes(), 0o644); err != nil { + fmt.Fprintf(os.Stderr, "Warning: failed to write _nav_fragment.yml: %v\n", err) + } +} diff --git a/TOOLS/docs-generator/main.go b/TOOLS/docs-generator/main.go index 946514f..9f44d27 100644 --- a/TOOLS/docs-generator/main.go +++ b/TOOLS/docs-generator/main.go @@ -75,6 +75,7 @@ func main() { processedSpecs = append(processedSpecs, spec) } writers.IndexMD(docsDir, processedSpecs) + writers.WriteNavFragment(docsDir, processedSpecs) } func toSet(csv string) map[string]bool { diff --git a/mkdocs.yml b/mkdocs.yml index cf691d7..922859d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,10 +24,11 @@ theme: primary: blue accent: indigo features: - - navigation.tabs - - navigation.tabs.sticky - navigation.sections - navigation.top + - navigation.path + - navigation.footer + - navigation.indexes extra_css: - 30_registry/assets/extra.css