Перейти к содержимому

Справочник Render

dwe render производит файлы, выведенные из объединённой конфигурации DWE. Это единая точка входа для сгенерированных артефактов — ни один из этих файлов не должен править вручную; вместо этого перезапустите соответствующую подкоманду.

КомандаВыводИсточник
dwe render envсодержимое .env (stdout или --out <path>)правила exports.env в workspace/defaults.yml + системные переменные
dwe render ideIDE-файлы по каждому сервису внутри hub-каталога сервисапакеты шаблонов в workspace/templates/ide/<pack>/, управляемые manifest.yml
dwe render aiagent-доки на уровне hub (AGENTS.md, симлинк CLAUDE.md, …)пакеты шаблонов в workspace/templates/ai/<pack>/, управляемые manifest.yml
dwe render gitshell git-хуки на каждый сервис, в <svc.Dir>/src/.git/hooks/<basename> (режим 0755)пакеты шаблонов в workspace/templates/git/<pack>/, управляемые manifest.yml
dwe render configconfig-файлы по каждому сервису (.env, env.php, …) внутри hub-каталога сервиса, с воспроизведением собранных секретовпакеты шаблонов в workspace/templates/config/<pack>/, управляемые manifest.yml

Все пять подкоманд читают одну и ту же объединённую конфигурацию (workspace.ymlworkspace/defaults.ymlworkspace/local.yml, с объявлениями сервисов из workspace/services/<name>/service.yml). Различаются они тем, что итерируют и куда пишут.

flowchart LR
  L1[workspace.yml] --> M
  L2[workspace/defaults.yml] --> M
  L3[workspace/local.yml] --> M
  S["workspace/services/*/service.yml"] --> M
  M[("Объединённая конфигурация")]

  M --> E[render env]
  M --> I[render ide]
  M --> A[render ai]
  M --> G[render git]
  M --> C[render config]

  E --> EOUT[".env / stdout"]
  I --> IOUT["services/{name}/..."]
  A --> AOUT["services/{name}/AGENTS.md<br/>services/{name}/CLAUDE.md<br/>..."]
  G --> GOUT["services/{name}/src/.git/hooks/...<br/>(режим 0755)"]
  C --> COUT["services/{name}/.env<br/>services/{name}/env.php<br/>..."]

Каждая подкоманда:

  1. Загружает объединённую конфигурацию. Отсутствующая или невалидная проектная конфигурация — жёсткая ошибка.
  2. Выбирает цели:
    • env — один артефакт, без выборки.
    • ide / ai / git / config — итерирует сервисы, применяет политику выборки, опционально сужает до одного сервиса через аргумент [service].
  3. Пишет выходные файлы. Куда они идут — зависит от подкоманды:
    • render ide и render ai пишут внутрь hub-каталога каждого сервиса, привязанного к корню проекта (каталог, содержащий workspace.yml), и применяют границы безопасности путей.
    • render git пишет в <svc.Dir>/src/.git/hooks/ для каждого сервиса, у которого src/.git — реальный каталог; назначение никогда не отслеживается git.
    • render config пишет runtime config-файлы по каждому сервису (.env, env.php, …) внутрь hub-каталога каждого сервиса из workspace/templates/config/<pack>/, воспроизводя собранные секреты; app-сервисы итерируются в порядке DeployOrder.
    • render env пишет в stdout по умолчанию или в аргумент --out <path> как задано. Путь --out трактуется относительно текущего рабочего каталога, не корня проекта — указывайте абсолютный путь, если нужно детерминированное расположение независимо от того, откуда запущена команда.
Аспектrender envrender iderender airender gitrender config
Итерирует сервисынетдададада (app-сервисы, DeployOrder)
Читает шаблоны с дисканетда (через manifest)да (через manifest)да (через manifest)да (через manifest)
Пер-сервисное поле opt-inservices.<name>.render.ide.enabledservices.<name>.render.ai.enabledservices.<name>.render.git.enabledразрешимый config-пакет (только app)
Политика opt-in по умолчаниюtrue для type: app; false иначеtrue для type: app; false иначеtrue для type: app; false иначетолько app-сервисы; нет пакета → молча no-op
Политика коллизий при общем dirвыигрывает самый глубокий extends (per-variant override)выигрывает самый поверхностный extends (каноническая идентичность hub)выигрывает самый глубокий extends (per-variant хуки)hub родителя extends рендерится один раз (alias пропускается)
Файл manifestmanifest.yml объявляет render (+ symlinks)manifest.yml объявляет render + symlinksmanifest.yml объявляет только rendermanifest.yml объявляет только render
Поддерживаются симлинкинетда (относительные, внутри hub)да (относительные, внутри hub)нет — to должен быть basenameнет — отвергаются
Режим выводаn/aкак написанокак написаноявный chmod 0755 на каждый прогонreplace (перезапись)
Защита путейn/aотказ от симлинков в пакете и назначенииотказ от симлинков в пакете и назначенииpreflight hub-а + отказ от симлинков в .git/hooks/отказ от симлинков в пакете и назначении

render ide, render ai и render git читают manifest.yml в корне выбранного пакета шаблонов по единой общей схеме:

render:
- from: <путь внутри пакета, оканчивающийся на .tmpl>
to: <путь назначения относительно dest-root конкретного типа>
symlinks:
- link: <путь симлинка>
to: <существующий путь рендера>

Поверх — ограничения по типу:

ТипDest rootФорма tosymlinks
idehub-каталог сервисалюбой содержащийся относительный путьразрешены
aihub-каталог сервисалюбой содержащийся относительный путьразрешены, должны ссылаться на to из render
git<svc.Dir>/src/.git/hooks/только basename (без слешей, без ..)отвергаются — должны быть пусты

Manifest загружается со строгим YAML-декодом (yaml.Decoder.KnownFields(true)); неизвестные поля — жёсткая ошибка. Пустой manifest (без render и symlinks) отвергается. Валидация разделена на shape (чистая, без файловой системы) и sources (resolver-aware проверка существования), чтобы shadow-pack override участвовал в валидации существования источников ровно так же, как их читает рендерер.

Любой пакет шаблонов workspace/templates/<kind>/<pack>/<rel> может быть пофайлово переопределён соседним shadow-пакетом workspace/templates/<kind>/<pack>.local/<rel>. Resolver, применяемый всеми тремя подкомандами рендера:

  1. Проверить workspace/templates/<kind>/<pack>.local/<rel>:
    • обычный файл → использовать; рендерер выводит одну info-строку using local override: workspace/templates/<kind>/<pack>.local/<rel>.
    • существует, но это каталог или симлинк → жёсткая ошибка; override не падает молча на канонический пакет (так плохой override обозначит себя сам).
    • отсутствует → провалиться дальше.
  2. Проверить workspace/templates/<kind>/<pack>/<rel>:
    • обычный файл → использовать.
    • существует, но это каталог или симлинк → жёсткая ошибка с именем нарушающего пути.
    • отсутствует → обёрнутый os.ErrNotExist.

Каталог <pack>.local/ — это сосед канонического пакета, не его потомок. Он лежит в отслеживаемом workspace/templates/<kind>/ и игнорируется git по паттерну (workspace/templates/*/*.local/ или более широкое правило *.local/ — рекомендуется добавить в проектный .gitignore).

В override-пакете должны быть только переопределяемые файлы — это не полный пакет. manifest.yml читается только из канонического пакета; override не может переписать manifest, а только подменить отдельные from:-источники.

Это зеркалит существующее в проекте соглашение про user-local override:

Каноническое (отслеживаемое)Локальный сосед (gitignored)
workspace/workspace.ymlworkspace/local.yml (описан в справочнике services)
workspace/docker.ymlworkspace/docker.local.yml
workspace/templates/<kind>/<pack>/workspace/templates/<kind>/<pack>.local/

.dwe/ (runtime-каталог) никогда не используется для пользовательских оверрайдов — он зарезервирован под управляемое DWE состояние (deploy/state.yml, deploy/deploy.lock, logs/).

Override — это подмена входа, а не перенаправление выхода:

  • Файл-override workspace/templates/<kind>/<pack>.local/<rel> игнорируется git по паттерну .local/ и никогда не коммитится.
  • Отрендеренный выход всё равно падает на to, объявленный в manifest.

Что это означает на практике:

ТипПуть выводаОтслеживается?Эффект локального override
git<svc.Dir>/src/.git/hooks/<basename>никогда (внутри .git/)override полностью приватен для разработчика
ide / ai<svc.Dir>/<rel> (обычно отслеживается)как правило даперерендер меняет отслеживаемый артефакт; разработчик сам отвечает за то, чтобы не закоммитить эти изменения (git stash, git checkout -- <path> или личный pre-commit guard)

Для IDE/AI локальный override, дающий другой выход, — это поток, в который вы намеренно входите; держите его вне коммитов так же, как и любую несвязанную WIP-правку.

  • render env — генерация .env: системные переменные, правила экспорта, фильтрация через when, форматирование значений
  • render ide — IDE-пакеты шаблонов: разрешение пакета, схема manifest, политика «глубочайший выигрывает», пер-сервисный рендер
  • render ai — пакеты agent-доков: схема manifest, политика «поверхностнейший выигрывает», записи render + symlinks
  • render git — shell git-хуки: manifest-driven рендер в <svc.Dir>/src/.git/hooks/, «глубочайший выигрывает», режим 0755
  • render config — config-файлы сервисов: подложка ${...}, воспроизведение ${generated.<name>}, секреты по принципу «собрать, а не выпустить», opt-in разрешение пакетов
  • workspace.yml / defaults.yml / local.yml — слои объединённой конфигурации и разрешение dot-path (используется render env)
  • определения сервисов (workspace/services/*/service.yml) — определения сервисов, блоки ide / ai / git, цепочки extends
  • Шаблоны — синтаксис Go-шаблонов, помощники sprout, render-контекст (общий с info / commands / pipelines)
  • Запустите dwe render --help (или dwe render <подкоманда> --help), чтобы увидеть актуальный CLI-интерфейс