refactor: merge ops-generator into docs-generator as --ops mode

Single binary, single go.mod — no divergence risk.
Run: docs-generator --ops (or regular docs-generator for resource docs)
This commit is contained in:
“Naeel”
2026-07-05 10:28:59 +04:00
parent 46e847a3c1
commit 6a1b243366
6 changed files with 71 additions and 123 deletions
@@ -1,13 +1,11 @@
// Package docs — генерация ops-документации.
package docs
// Package ops — генерация per-service operations документации.
package ops
import (
"encoding/json"
"fmt"
"os"
"strings"
"ops-generator/internal/types"
)
// Entry — запись в индексе документации.
@@ -20,7 +18,7 @@ type Entry struct {
}
// ServiceDoc генерирует Markdown-документацию операций сервиса.
func ServiceDoc(path string, spec types.ServiceOpsSpec) error {
func ServiceDoc(path string, spec ServiceOpsSpec) error {
var b strings.Builder
b.WriteString(fmt.Sprintf("# Operations for nubes_%s\n\n", spec.Name))
b.WriteString(fmt.Sprintf("Service ID: `%d`\n\n", spec.ServiceID))
@@ -110,7 +108,7 @@ func Name(name string) string {
}
// Title возвращает отображаемое имя сервиса.
func Title(spec types.ServiceOpsSpec) string {
func Title(spec ServiceOpsSpec) string {
if strings.TrimSpace(spec.ServiceDisplayName) != "" {
return spec.ServiceDisplayName
}
@@ -1,5 +1,5 @@
// Package types — структуры данных для ops-документации.
package types
// Package ops — структуры данных для ops-документации (операции сервисов).
package ops
// ServiceOpsSpec — YAML-спек операций сервиса.
type ServiceOpsSpec struct {
+65
View File
@@ -14,6 +14,7 @@ import (
"gopkg.in/yaml.v3"
"docs-generator/internal/ops"
"docs-generator/internal/types"
"docs-generator/internal/writers"
)
@@ -25,9 +26,16 @@ func main() {
excludeFlag := flag.String("exclude", "", "Comma-separated resource names to skip")
versionFlag := flag.String("version", "", "Provider version for example block")
apiEndpointFlag := flag.String("api-endpoint", "https://deck-api.ngcloud.ru/api/v1/index.cfm", "API endpoint for example block")
opsFlag := flag.Bool("ops", false, "Generate per-service operations docs (resources_ops_yaml → docs/.../operations)")
flag.Parse()
root := detectRoot()
// --ops mode: generate per-service operations documentation
if *opsFlag {
runOpsMode(root)
return
}
resourcesDir := pickPath(*resourcesDirFlag, filepath.Join(root, "provider", "resources_yaml"))
docsDir := pickPath(*docsDirFlag, filepath.Join(root, "docs", "30_registry", "resources"))
servicesList := pickPath(*servicesListFlag, filepath.Join(root, "devops", "config", "services_list.txt"))
@@ -171,3 +179,60 @@ func loadSpecs(dir string, ordered []types.ServiceMeta) []types.ServiceSpec {
}
return specs
}
// runOpsMode генерирует per-service operations документацию.
func runOpsMode(root string) {
yamlDir := pickPath(os.Getenv("NUBES_OPS_YAML_DIR"), filepath.Join(root, "provider", "resources_ops_yaml"))
docsDir := pickPath(os.Getenv("NUBES_OPS_DOCS_DIR"), filepath.Join(root, "docs", "30_registry", "resources", "operations"))
if err := os.MkdirAll(docsDir, 0o755); err != nil {
panic(err)
}
entries := []ops.Entry{}
err := filepath.WalkDir(yamlDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") {
return nil
}
b, err := os.ReadFile(path)
if err != nil {
return err
}
var spec ops.ServiceOpsSpec
if err := yaml.Unmarshal(b, &spec); err != nil {
return err
}
if spec.ServiceID == 0 || spec.Name == "" {
return nil
}
fileName := fmt.Sprintf("%d_%s.md", spec.ServiceID, ops.Name(spec.Name))
outPath := filepath.Join(docsDir, fileName)
if err := ops.ServiceDoc(outPath, spec); err != nil {
return err
}
entries = append(entries, ops.Entry{
ServiceID: spec.ServiceID,
Name: spec.Name,
File: fileName,
Title: ops.Title(spec),
OpCount: len(spec.Operations),
})
return nil
})
if err != nil {
panic(err)
}
sort.Slice(entries, func(i, j int) bool { return entries[i].ServiceID < entries[j].ServiceID })
if err := ops.Index(filepath.Join(docsDir, "index.md"), entries); err != nil {
panic(err)
}
fmt.Printf("Generated %d operations docs in %s\n", len(entries), docsDir)
}
-5
View File
@@ -1,5 +0,0 @@
module ops-generator
go 1.24
require gopkg.in/yaml.v3 v3.0.1
-4
View File
@@ -1,4 +0,0 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-106
View File
@@ -1,106 +0,0 @@
// ops-generator — генератор per-service operations документации.
package main
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"gopkg.in/yaml.v3"
"ops-generator/internal/docs"
"ops-generator/internal/types"
)
func main() {
repoRoot, err := findRepoRoot()
if err != nil {
panic(err)
}
yamlDir := strings.TrimSpace(os.Getenv("NUBES_OPS_YAML_DIR"))
if yamlDir == "" {
yamlDir = filepath.Join(repoRoot, "resources_ops_yaml")
}
docsDir := strings.TrimSpace(os.Getenv("NUBES_OPS_DOCS_DIR"))
if docsDir == "" {
root := filepath.Dir(repoRoot)
docsDir = filepath.Join(root, "docs", "30_registry", "resources", "operations")
}
if err := os.MkdirAll(docsDir, 0o755); err != nil {
panic(err)
}
entries := []docs.Entry{}
err = filepath.WalkDir(yamlDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") {
return nil
}
b, err := os.ReadFile(path)
if err != nil {
return err
}
var spec types.ServiceOpsSpec
if err := yaml.Unmarshal(b, &spec); err != nil {
return err
}
if spec.ServiceID == 0 || spec.Name == "" {
return nil
}
fileName := fmt.Sprintf("%d_%s.md", spec.ServiceID, docs.Name(spec.Name))
outPath := filepath.Join(docsDir, fileName)
if err := docs.ServiceDoc(outPath, spec); err != nil {
return err
}
entries = append(entries, docs.Entry{
ServiceID: spec.ServiceID,
Name: spec.Name,
File: fileName,
Title: docs.Title(spec),
OpCount: len(spec.Operations),
})
return nil
})
if err != nil {
panic(err)
}
sort.Slice(entries, func(i, j int) bool { return entries[i].ServiceID < entries[j].ServiceID })
if err := docs.Index(filepath.Join(docsDir, "index.md"), entries); err != nil {
panic(err)
}
fmt.Printf("Generated %d operations docs in %s\n", len(entries), docsDir)
}
func findRepoRoot() (string, error) {
wd, err := os.Getwd()
if err != nil {
return "", err
}
current := wd
for i := 0; i < 8; i++ {
candidate := filepath.Join(current, "resources_yaml")
info, err := os.Stat(candidate)
if err == nil && info.IsDir() {
return current, nil
}
parent := filepath.Dir(current)
if parent == current {
break
}
current = parent
}
return "", fmt.Errorf("failed to locate provider repo root")
}