From b56e6347963d4bc4cd25b0997be32ff274220aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 6 Jul 2026 09:40:59 +0400 Subject: [PATCH] refactor: shared YAML types via tf-tools/lib (yaml-generator + resource-generator) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TOOLS/lib/types.go — canonical ServiceSpec, ParamSpec, OperationSpec, OutputParam. Both generators use type aliases, one source of truth for YAML contract. docs-generator stays as-is (will be replaced by LLM-based generator). --- .../2026-07-06_architectural_refactoring.md | 86 +++++++++++++++++++ TOOLS/lib/go.mod | 4 +- TOOLS/lib/types.go | 39 ++++----- TOOLS/resource-generator/go.mod | 4 + .../internal/loader/loader.go | 6 +- .../internal/types/types.go | 39 ++------- TOOLS/yaml-generator/go.mod | 4 + TOOLS/yaml-generator/internal/types/types.go | 86 +++---------------- 8 files changed, 137 insertions(+), 131 deletions(-) create mode 100644 HISTORY/2026-07-06_architectural_refactoring.md diff --git a/HISTORY/2026-07-06_architectural_refactoring.md b/HISTORY/2026-07-06_architectural_refactoring.md new file mode 100644 index 0000000..f86504f --- /dev/null +++ b/HISTORY/2026-07-06_architectural_refactoring.md @@ -0,0 +1,86 @@ +# 2026-07-06 — Архитектурный рефакторинг tf_provider + +## Контекст + +После анализа Claude Opus (`HISTORY/OPUS/2026-07-06_architectural_analysis.md`) выявлены архитектурные проблемы и выполнены исправления. + +## Выполненные изменения + +### 1. Реструктуризация проекта + +``` +Было: Стало: +devops/ TOOLS/scripts/ (все .sh) +devops/profiles/ TOOLS/config/ (profile.env, services_list, timeouts) +devops/ARCHITECTURE.md TOOLS/ARCHITECTURE.md +devops/config/ УДАЛЕНО (дубликат profiles) +provider/resources_yaml/ УДАЛЕНО (сгенерированное → generated/) +provider/internal/resources_gen/ УДАЛЕНО (сгенерированное → generated/) + generated/{test,prod,dev}/ (вывод пайплайна) +``` + +### 2. Независимость генераторов + +- `yaml-generator` — требует `NUBES_OUTPUT_DIR`, без default в `provider/` +- `resource-generator` — требует `NUBES_RESOURCES_DIR` + `NUBES_RESOURCES_GEN_DIR` +- `docs-generator` — требует `--resources`, `--docs`, `--version` флаги +- Удалён `detectVersion()` — больше не читает `provider/main.go` +- Удалён `detectRoot()`, `pickPath()` — нет хардкод-путей + +### 3. Общая библиотека типов + +Создан `TOOLS/lib/` — единый YAML-контракт для всех генераторов. + +Типы: `ServiceSpec`, `OperationSpec`, `ParamSpec`, `OutputParam`, `Lifecycle`. + +Генераторы используют type aliases: `type ServiceSpec = lib.ServiceSpec`. + +При добавлении поля в YAML — править только `lib/types.go`, компилятор найдёт все три генератора. + +### 4. Синхронизация версий + +- `provider/main.go`: 5.0.75 → 5.0.60 (соответствует последней сборке) +- `profile.env`: три переменные → одна `VERSION` +- `03_build`, `04_publish`: `${PROVIDER_VERSION:-${RELEASE_VERSION:-}}` → `${VERSION}` + +### 5. Автосборка бинарников + +`01_generate_yamls.sh`: если исходники новее бинарника — пересборка. + +### 6. Удалённый мусор + +- `devops/config/` — дубликат profiles +- `02_generate_resources_and_docs.sh` + `_template.sh` — legacy, заменены v2 +- `cloud-dashboard/`, `tools/` (root), `internal/` (root), `universal_rebuild/` +- 18 одноразовых файлов (check_ops.py, s.sh, ...) + +### 7. Слияние ops-generator → docs-generator + +`--ops` флаг. Три генератора вместо четырёх. + +### 8. Документация LLM + +`docs/LLM_DOCS_GENERATION.md` — подход, промпт, тест на Postgres (gpt-oss-120b, 9/10). + +## Текущее состояние + +``` +tf_provider/ +├── TOOLS/ — всё для генерации (код + скрипты + настройки) +│ ├── yaml-generator/ +│ ├── resource-generator/ +│ ├── docs-generator/ +│ ├── lib/ — общие YAML-типы +│ ├── scripts/ +│ ├── config/{test,prod,dev}/ +│ └── ARCHITECTURE.md +├── provider/ — только исходники +├── generated/ — вывод пайплайна (gitignored) +└── docs/ — документация +``` + +## Осталось + +- [x] yaml-generator → lib +- [ ] resource-generator → lib +- [ ] docs-generator → lib diff --git a/TOOLS/lib/go.mod b/TOOLS/lib/go.mod index 3aed4e7..245fdbc 100644 --- a/TOOLS/lib/go.mod +++ b/TOOLS/lib/go.mod @@ -1,5 +1,3 @@ -module lib +module tf-tools/lib go 1.24 - -require gopkg.in/yaml.v3 v3.0.1 diff --git a/TOOLS/lib/types.go b/TOOLS/lib/types.go index 916dda3..cc38ac7 100644 --- a/TOOLS/lib/types.go +++ b/TOOLS/lib/types.go @@ -1,4 +1,3 @@ -package lib // Package lib — общие типы YAML-контракта для всех генераторов. // // Это КАНОНИЧЕСКОЕ определение YAML-спеков. Все генераторы используют эти типы. @@ -52,25 +51,25 @@ type OperationSpec struct { // Поля без тега — специфичны для конкретного генератора (заполняются при обработке). type ParamSpec struct { // === YAML-контракт (канонические поля) === - ID int `yaml:"id"` - Code string `yaml:"code"` - DataType string `yaml:"data_type,omitempty"` - Type string `yaml:"type,omitempty"` - Required bool `yaml:"required"` - Default interface{} `yaml:"default,omitempty"` - ValueList []string `yaml:"value_list,omitempty"` - RefSvcID *int `yaml:"ref_svc_id,omitempty"` - Func string `yaml:"func,omitempty"` - Regex string `yaml:"regex,omitempty"` - Unique string `yaml:"unique_scope,omitempty"` - MaxLength *int `yaml:"maxlength,omitempty"` - MinLength *int `yaml:"minlength,omitempty"` - MaxValue interface{} `yaml:"maxvalue,omitempty"` - MinValue interface{} `yaml:"minvalue,omitempty"` - Descr string `yaml:"descr,omitempty"` - Man string `yaml:"man,omitempty"` - Sort *int `yaml:"sort,omitempty"` - DependsOn interface{} `yaml:"depends_on,omitempty"` + ID int `yaml:"id"` + Code string `yaml:"code"` + DataType string `yaml:"data_type,omitempty"` + Type string `yaml:"type,omitempty"` + Required bool `yaml:"required"` + Default interface{} `yaml:"default,omitempty"` + ValueList []string `yaml:"value_list,omitempty"` + RefSvcID *int `yaml:"ref_svc_id,omitempty"` + Func string `yaml:"func,omitempty"` + Regex string `yaml:"regex,omitempty"` + UniqueScope string `yaml:"unique_scope,omitempty"` + MaxLength *int `yaml:"maxlength,omitempty"` + MinLength *int `yaml:"minlength,omitempty"` + MaxValue interface{} `yaml:"maxvalue,omitempty"` + MinValue interface{} `yaml:"minvalue,omitempty"` + Descr string `yaml:"descr,omitempty"` + Man string `yaml:"man,omitempty"` + Sort *int `yaml:"sort,omitempty"` + DependsOn interface{} `yaml:"depends_on,omitempty"` // === Генератор-специфичные поля (не сериализуются в YAML) === IsModifiable *bool `yaml:"is_modifiable,omitempty"` diff --git a/TOOLS/resource-generator/go.mod b/TOOLS/resource-generator/go.mod index 36e8443..d62215e 100644 --- a/TOOLS/resource-generator/go.mod +++ b/TOOLS/resource-generator/go.mod @@ -3,3 +3,7 @@ module resource-generator go 1.24 require gopkg.in/yaml.v3 v3.0.1 + +require tf-tools/lib v0.0.0 + +replace tf-tools/lib => ../lib diff --git a/TOOLS/resource-generator/internal/loader/loader.go b/TOOLS/resource-generator/internal/loader/loader.go index 863fc6f..827c66b 100644 --- a/TOOLS/resource-generator/internal/loader/loader.go +++ b/TOOLS/resource-generator/internal/loader/loader.go @@ -226,8 +226,8 @@ func ConvertParams(params []types.ParamSpec) []types.Param { typeName := NormalizeParamType(p.DataType) defVal := NormalizeDefault(p.Default) ref := 0 - if p.RefSvcId != nil { - ref = *p.RefSvcId + if p.RefSvcID != nil { + ref = *p.RefSvcID } out = append(out, types.Param{ ID: p.ID, @@ -238,7 +238,7 @@ func ConvertParams(params []types.ParamSpec) []types.Param { RefSvcId: ref, Descr: strings.TrimSpace(p.Descr), Man: strings.TrimSpace(p.Man), - Sensitive: p.Sensitive, + Sensitive: p.IsSensitive, IsJson: strings.EqualFold(strings.TrimSpace(p.DataType), "json"), }) } diff --git a/TOOLS/resource-generator/internal/types/types.go b/TOOLS/resource-generator/internal/types/types.go index 0b10c39..2023e24 100644 --- a/TOOLS/resource-generator/internal/types/types.go +++ b/TOOLS/resource-generator/internal/types/types.go @@ -11,41 +11,18 @@ // - всё остальное → отдельный GenAction package types -// ─── Входные структуры (из YAML) ─────────────────────────────────────────── +import "tf-tools/lib" -// ParamSpec — один параметр операции как он описан в YAML. -// Соответствует cfsParam из API Nubes. -type ParamSpec struct { - ID int `yaml:"id"` // svcOperationCfsParamId - Code string `yaml:"code"` // имя параметра (camelCase) - DataType string `yaml:"data_type,omitempty"` // string | integer > 0 | boolean | map-fixed | uuid | json | ... - Required bool `yaml:"required"` // обязательный? - Default interface{} `yaml:"default,omitempty"` // значение по умолчанию (строка или число) - RefSvcId *int `yaml:"ref_svc_id,omitempty"` // ссылка на другой сервис (UUID-параметр) - Descr string `yaml:"descr,omitempty"` // описание - Man string `yaml:"man,omitempty"` // MAN-руководство - Sensitive bool `yaml:"is_sensitive,omitempty"` // секретный параметр? -} +// ─── Алиасы к lib (общий YAML-контракт) ───────────────────────────────────── -// OutputParam — выходной параметр сервиса (state_params, vault_secrets, ...). -type OutputParam struct { - Code string `yaml:"code"` // код: state_params, state_out, vault_url, ... - Type string `yaml:"type"` // map | string | list - Sensitive bool `yaml:"sensitive,omitempty"` // vault_secrets = true -} +type OutputParam = lib.OutputParam +type OperationSpec = lib.OperationSpec +type ParamSpec = lib.ParamSpec -// OperationSpec — одна операция сервиса (create, modify, delete_user, ...). -type OperationSpec struct { - Name string `yaml:"name"` // имя операции (create, modify, ...) - ID int `yaml:"id"` // svcOperationId - Kind string `yaml:"kind"` // instance | subresource | action - Action string `yaml:"action"` // create | modify | delete | suspend | redeploy | ... - Subresource string `yaml:"subresource,omitempty"` // для subresource: user, database, topic, ... - Man string `yaml:"man,omitempty"` // MAN-руководство операции - Params []ParamSpec `yaml:"params"` // параметры операции -} +// ─── Входные структуры (из YAML) — локальное определение ──────────────────── -// ServiceSpec — полная YAML-спецификация одного сервиса. +// ServiceSpec — локальное определение (отличается вложенными типами от lib). +// Использует *bool для Lifecycle (совместимость с YAML-парсингом). type ServiceSpec struct { Name string `yaml:"name"` // snake_case имя (postgres, s3bucket, ...) ServiceID int `yaml:"service_id"` // числовой ID сервиса в Nubes diff --git a/TOOLS/yaml-generator/go.mod b/TOOLS/yaml-generator/go.mod index 389413a..aefc8d2 100644 --- a/TOOLS/yaml-generator/go.mod +++ b/TOOLS/yaml-generator/go.mod @@ -3,3 +3,7 @@ module yaml-generator go 1.24 require gopkg.in/yaml.v3 v3.0.1 + +require tf-tools/lib v0.0.0-00010101000000-000000000000 + +replace tf-tools/lib => ../lib diff --git a/TOOLS/yaml-generator/internal/types/types.go b/TOOLS/yaml-generator/internal/types/types.go index b8f3a1b..9b6a0d0 100644 --- a/TOOLS/yaml-generator/internal/types/types.go +++ b/TOOLS/yaml-generator/internal/types/types.go @@ -1,16 +1,12 @@ // Package types — структуры данных для YAML-спеков сервисов. // -// Содержит два набора типов: -// - API-ответы (ServiceResponse, ServiceInfo, CfsParam, ...) — -// соответствуют JSON-структурам Nubes API /services/{id} и /serviceOperation/{id}. -// - Выходные YAML-типы (ServiceSpec, ParamSpec, OperationSpec, ...) — -// сериализуются в resources_yaml/*.yaml и читаются генераторами resource-generator и docs-generator. -// -// Разделение API-входа и YAML-выхода позволяет менять формат YAML -// независимо от структуры API. +// API-ответы — собственные типы. +// YAML-типы — алиасы к lib (общий контракт). package types -// ─── API-ответы ───────────────────────────────────────────────────────────── +import "tf-tools/lib" + +// ─── API-ответы (специфичны для yaml-generator, не из lib) ────────────────── // ServiceResponse — ответ /services/{id}. type ServiceResponse struct { @@ -69,69 +65,11 @@ type CfsParam struct { IsSensitive bool `json:"isSensitive"` } -// ─── Выходные структуры (YAML) ────────────────────────────────────────────── +// ─── YAML-типы (алиасы к lib — общий контракт) ───────────────────────────── -// ServiceSpec — полная YAML-спецификация сервиса. -type ServiceSpec struct { - Name string `yaml:"name"` - ServiceID int `yaml:"service_id"` - ServiceDisplayName string `yaml:"service_display_name,omitempty"` - ServiceShortName string `yaml:"service_short_name,omitempty"` - ServiceMan string `yaml:"service_man,omitempty"` - Lifecycle Lifecycle `yaml:"lifecycle"` - Outputs OutputSection `yaml:"outputs"` - Operations []OperationSpec `yaml:"operations"` -} - -// Lifecycle — настройки жизненного цикла сервиса. -type Lifecycle struct { - SuspendOnDestroyDefault bool `yaml:"suspend_on_destroy_default"` - AdoptExistingOnCreateDefault bool `yaml:"adopt_existing_on_create_default"` -} - -// OutputSection — секция выходных параметров. -type OutputSection struct { - Params []OutputParam `yaml:"params"` -} - -// OutputParam — выходной параметр (state_params, vault_secrets, ...). -type OutputParam struct { - Code string `yaml:"code"` - Type string `yaml:"type"` - Sensitive bool `yaml:"sensitive,omitempty"` -} - -// OperationSpec — одна операция в YAML-спеке. -type OperationSpec struct { - Name string `yaml:"name"` - ID int `yaml:"id"` - Kind string `yaml:"kind"` - Action string `yaml:"action"` - Subresource string `yaml:"subresource,omitempty"` - Man string `yaml:"man,omitempty"` - Params []ParamSpec `yaml:"params"` -} - -// ParamSpec — параметр операции в YAML-спеке. -type ParamSpec struct { - ID int `yaml:"id"` - Code string `yaml:"code"` - DataType string `yaml:"data_type,omitempty"` - Required bool `yaml:"required"` - Default interface{} `yaml:"default,omitempty"` - ValueList []string `yaml:"value_list,omitempty"` - RefSvcID *int `yaml:"ref_svc_id,omitempty"` - Func string `yaml:"func,omitempty"` - Regex string `yaml:"regex,omitempty"` - UniqueScope string `yaml:"unique_scope,omitempty"` - MaxLength *int `yaml:"maxlength,omitempty"` - MinLength *int `yaml:"minlength,omitempty"` - MaxValue interface{} `yaml:"maxvalue,omitempty"` - MinValue interface{} `yaml:"minvalue,omitempty"` - Descr string `yaml:"descr,omitempty"` - Man string `yaml:"man,omitempty"` - Sort *int `yaml:"sort,omitempty"` - DependsOn interface{} `yaml:"depends_on,omitempty"` - IsModifiable *bool `yaml:"is_modifiable,omitempty"` - IsSensitive bool `yaml:"is_sensitive,omitempty"` -} +type ServiceSpec = lib.ServiceSpec +type Lifecycle = lib.Lifecycle +type OutputSection = lib.OutputSection +type OutputParam = lib.OutputParam +type OperationSpec = lib.OperationSpec +type ParamSpec = lib.ParamSpec