Files
tf_provider/TOOLS/docs-generator/main.go
T
“Naeel” 5da22335ae refactor: rename universal_rebuild/ → provider/
- Rename directory
- Update all 9 devops scripts
- Update 4 generator source files
- Rebuild all binaries
2026-07-05 10:20:04 +04:00

174 lines
4.3 KiB
Go

// docs-generator — генератор Markdown-документации для ресурсов провайдера.
package main
import (
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"gopkg.in/yaml.v3"
"docs-generator/internal/types"
"docs-generator/internal/writers"
)
func main() {
resourcesDirFlag := flag.String("resources", "", "Path to resources_yaml")
docsDirFlag := flag.String("docs", "", "Path to docs/30_registry/resources")
servicesListFlag := flag.String("services", "", "Path to devops/config/services_list.txt")
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")
flag.Parse()
root := detectRoot()
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"))
writers.CloudOutputByServiceID = writers.LoadCloudOutputSnapshot(root)
excludeSet := toSet(*excludeFlag)
version := *versionFlag
if version == "" {
version = detectVersion(filepath.Join(root, "provider", "main.go"))
}
apiEndpoint := *apiEndpointFlag
servicesOrder := loadServicesList(servicesList)
specs := loadSpecs(resourcesDir, servicesOrder)
if len(specs) == 0 {
fmt.Fprintf(os.Stderr, "No specs found in %s\n", resourcesDir)
os.Exit(1)
}
if err := os.MkdirAll(docsDir, 0o755); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: cannot create docs dir %s: %v\n", docsDir, err)
os.Exit(1)
}
var processedSpecs []types.ServiceSpec
for _, spec := range specs {
if excludeSet[spec.Name] {
continue
}
writers.ResourceDocs(docsDir, spec, version, apiEndpoint)
processedSpecs = append(processedSpecs, spec)
}
writers.IndexMD(docsDir, processedSpecs)
}
func detectRoot() string {
cwd, err := os.Getwd()
if err != nil {
panic(err)
}
if filepath.Base(cwd) == "provider" {
return filepath.Dir(cwd)
}
return cwd
}
func pickPath(value, fallback string) string {
if value != "" {
return value
}
return fallback
}
func toSet(csv string) map[string]bool {
out := map[string]bool{}
for _, item := range strings.Split(csv, ",") {
trimmed := strings.TrimSpace(item)
if trimmed == "" {
continue
}
out[trimmed] = true
}
return out
}
func detectVersion(mainPath string) string {
b, err := os.ReadFile(mainPath)
if err != nil {
return "2.x"
}
re := regexp.MustCompile(`version string\s*=\s*"([0-9.]+)"`)
match := re.FindStringSubmatch(string(b))
if len(match) > 1 {
return match[1]
}
return "2.x"
}
func loadServicesList(path string) []types.ServiceMeta {
b, err := os.ReadFile(path)
if err != nil {
return nil
}
lines := strings.Split(string(b), "\n")
out := []types.ServiceMeta{}
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
head := strings.SplitN(line, "#", 2)[0]
parts := strings.Fields(head)
if len(parts) < 2 {
continue
}
id, err := strconv.Atoi(parts[0])
if err != nil {
continue
}
out = append(out, types.ServiceMeta{ID: id, Name: parts[1]})
}
return out
}
func loadSpecs(dir string, ordered []types.ServiceMeta) []types.ServiceSpec {
specsByID := map[int]types.ServiceSpec{}
filepath.WalkDir(dir, 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.ServiceSpec
if err := yaml.Unmarshal(b, &spec); err != nil {
return err
}
specsByID[spec.ServiceID] = spec
return nil
})
if len(ordered) == 0 {
specs := make([]types.ServiceSpec, 0, len(specsByID))
for _, spec := range specsByID {
specs = append(specs, spec)
}
sort.Slice(specs, func(i, j int) bool { return specs[i].ServiceID < specs[j].ServiceID })
return specs
}
specs := []types.ServiceSpec{}
for _, meta := range ordered {
spec, ok := specsByID[meta.ID]
if !ok {
continue
}
specs = append(specs, spec)
}
return specs
}