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

Справочник полей сервиса

Каждое поле, допустимое в workspace/services/<name>/service.yml, плюс вложенные блоки (ports, hosts, icon, info, configs, dirs, cli, status, render, generated).

Терминология host vs internal: Поля, оканчивающиеся на *_internal или использующие суффиксное соглашение (например, dir для хоста, dir_internal для контейнера), относятся к путям: host-сторона работает на вашей машине, internal-сторона — точка монтирования в контейнере. То же различие применяется к портам и именам хостов: ports.http задаёт имя порта контейнера и хостовый порт, на котором он должен появиться; hosts.main — это имя, под которым контейнер резолвится.

ПолеТипОбязательноДопустимо дляОписание
typestringдаapp / tool / infraДискриминатор — выбирает список разрешённых полей для записи.
containerstringнет (по умолчанию имя папки)всеИмя compose-сервиса (по умолчанию имя папки). Команды для одного сервиса (logs, stop, restart, reset --service) находят запущенный контейнер по меткам compose (проект + сервис), поэтому пинить container_name в compose-файле под это имя не нужно — дефолтное именование compose <project>-<service>-<index> работает как есть.
requiredboolнетвсеЕсли true, сервис всегда включён; оверлей не может его выключить.
composelistнетвсеДополнительные файлы оверлея compose, активируемые при включении сервиса. Относительные пути внутри оверлея (bind-маунты, build.context, env_file) разрешаются от каталога первого файла в цепочке -f — compose.base, то есть от корня проекта при обычной раскладке, — а не от каталога самого оверлея: dwe не передаёт --project-directory, поэтому действует правило Compose для нескольких файлов. Сами пути в списке задаются относительно корня проекта; dwe validate предупреждает (config.compose_files), если такого файла нет, независимо от того, включён ли сервис.
compose_afterlistнетвсеФайлы оверлея compose, добавляемые после всех групп сервисов (tool → infra → app) и перед сгенерированным bridge-оверлеем и общепроектным compose.extra из local.yml — тот же гейт enabled, что у compose, один проход по именам сервисов среди всех типов, порядок из списка внутри сервиса сохраняется. Используйте это для патча, который должен побеждать собственный оверлей приложения (полные значения ключей вроде command:, healthcheck: или записи environment:, которые задают оба файла), а не проигрывать ему только из-за того, что группа app эмитится позже. Если два файла compose_after трогают один и тот же ключ, побеждает более поздний по алфавиту сервис — не полагайтесь на это, патчите непересекающиеся ключи. Это фиксирует порядок эмиссии, а не наличие патчимого сервиса: блок патча для приложения, которое сейчас отключено, всё равно заставит Compose отклонить весь проект целиком. Недоступно из слоёв-оверлеев (workspace.yml, defaults.yml, local.yml) — место этого поля в service.yml, как и у compose. Пути разрешаются так же, как у compose. В отличие от compose, это поле намеренно не отражается в смерженном конфиге — нет ни ${services.<name>.compose_after}, ни from: в exports.env, ни default_from: команды, которые могли бы его прочитать.
portsmap[string]int | map[string]{port,scheme}нетвсеИменованные порты контейнера. Краткая форма (int) или развёрнутая {port, scheme}. См. Поле ports.
hostsmap[string]stringнетвсеИменованные имена хостов. См. Поле hosts.
iconstringнетвсеВизуальный индикатор — эмодзи или символ, используемый в дашборде dwe info. Если опущен, используется значение по умолчанию для типа: type: app → 📦, type: tool → 🔧, type: infra → 🧱. См. Поле icon.
infoblockнетвсеМетаданные отображения для дашборда info — переопределение заголовка, выбор ключа host/port и подпути. См. Блок info.
depends_onlistнетapp / infraУпорядоченная зависимость от других сервисов (влияет на порядок deploy, а также расширяет область env.ports_free при по-сервисном deploy до сервисов, которые compose поднимает вместе с запрошенным). Цель type: tool отклоняется при загрузке.
statuslistнетвсеПользовательские колонки для таблицы dwe status apps / tools / infra по типу — см. Блок status.
on_enableblockнетapp / tool / infraХуки жизненного цикла при включении сервиса. См. Примеры — жизненный цикл переключения.
on_disableblockнетapp / tool / infraХуки жизненного цикла при выключении сервиса.
notesblockнетapp / tool / infraЧеловекочитаемые подсказки, отображаемые в плане команд services enable/disable.
dirstringда (без extends)appПуть к hub-каталогу сервиса на хосте.
dir_internalstringнетappТочка монтирования hub в контейнере.
work_dir_internalstringнетappРабочий каталог по умолчанию для exec/run внутри контейнера.
extendsstringнетappНаследует поля из другой записи type: app. Межтиповой extends отклоняется. См. Наследование.
configslistнетapp⚠️ Устарело — механизм копирования; мигрируйте на render.config. См. Поле configs.
dirslistнетappДополнительные каталоги относительно hub — см. Поле dirs.
cliblockнетappЗначения по умолчанию для dwe shell — см. Блок cli.
renderblockнетappВложенная политика рендера шаблонов (ide / ai / git / config) — см. Блок render.
generatedblockнетappPer-service значения, генерируемые сервисом, которые DWE собирает и переигрывает — см. Блок generated.
bridgeblockнетвсе (по умолчанию выкл. — строго opt-in)Opt-in host-моста — монтирует shim dwe в контейнер сервиса, чтобы dwe работал изнутри него. См. Блок bridge.

ports: всегда карта от имени порта к порту контейнера. Сервисам с одним портом требуется выбранное имя (рекомендация: http для веб, tcp для сырого TCP, ролевые имена вроде mysql / amqp для infra). Значения портов определяются в workspace/services/<name>/service.yml; оверлеи workspace/local.yml могут переназначать отдельные записи — см. поведение глубокого слияния в Поведение загрузки.

ports: — декларативные метаданные: сам по себе этот блок ничего не пробрасывает. Его читают dwe status, dwe info и preflight ports_free, но реальный биндинг живёт в вашем compose-файле, который получает значение через парное правило exports.env (from: services.<name>.ports.http) в workspace/defaults.yml. Порт, объявленный здесь без такого правила, существует только для отображения; dwe validate предупреждает о нём. См. exports.env.

Каждая запись порта принимает две равноценные формы:

  • Краткая (int) — номер порта. Применяется схема уровня сервиса (из info.scheme с фолбэком на runtime.use_https).
  • Развёрнутая (mapping) — {port: <int>, scheme: "http" | "https"}. Используйте, когда один сервис говорит по разным схемам на разных портах (например, API отдаёт HTTP на 3000 и HTTPS-админку на 9443).
workspace/services/rabbitmq/service.yml
type: infra
container: rabbitmq
ports:
amqp: 5672 # краткая форма — без переопределения схемы
admin: 15672
# workspace/services/api/service.yml — смешанные схемы внутри одного сервиса
type: app
container: api
ports:
http: 3000 # краткая: схема наследуется от info.scheme / runtime
admin: # развёрнутая: этот порт всегда https, независимо от глобального флага
port: 9443
scheme: https

Значения ограничены 1..65535 во время загрузки. Скалярные формы (ports: 80, ports: "80") отклоняются с ErrServicePortsShape. В развёрнутой форме допустимы только поля port и scheme; scheme должна быть "http" или "https" (или опущена).

Приоритет в оверлеях. Запись в workspace/local.yml может использовать любую из форм. Оверлеи мерджатся пополю с унаследованной спецификацией:

  • чистый int (http: 6000) — перекрывает только номер порта; scheme из service.yml сохраняется.
  • mapping (http: {port: 6000}) — то же, что и чистый int (затрагивает только port), полезно ради единообразия с rich-формой.
  • mapping только со scheme (http: {scheme: https}) — перекрывает только схему; унаследованный номер порта сохраняется. В rich-форме оверлея должно присутствовать хотя бы одно из полей port / scheme.
  • mapping с обоими полями (http: {port: 6000, scheme: https}) — перекрывает оба.

scheme: null трактуется как «нет переопределения» (эквивалентно отсутствию ключа).

Эффективная схема. Когда dwe рендерит URL для записи порта, он выбирает схему по цепочке приоритета:

  1. per-port scheme: (развёрнутая форма на этой записи);
  2. схема уровня сервиса info.scheme;
  3. глобальная runtime.use_https (true → https, false → http).

Это также доступно шаблонам через метод ServiceConfig.EffectiveScheme — см. Шаблоны. Для dot-path-доступа (from: / ${...}) per-port схемы доступны под соседним ключом services.<n>.port_schemes.<port-name> (string), который присутствует только у сервисов, фактически использующих переопределение.

URL’ы через reverse-proxy (port_via). Разрешение схемы для проксированного URL маршрутизируемого сервиса идёт по отдельной цепочке, которая намеренно пропускает info.scheme самого прокси (иначе схема прокси «протекла» бы на каждый маршрутизируемый сервис). Цепочка такая:

  1. info.scheme маршрутизируемого сервиса — задаёт и схему URL, и то, какой listener прокси (http или https) будет использован;
  2. per-port scheme: на записи listener’а прокси;
  3. глобальная runtime.use_https.

Это позволяет одному общему прокси обслуживать стек со смешанными схемами. Объявите ports.http: 80 и ports.https: 443 на прокси, затем выставьте info.scheme: https на тех маршрутизируемых сервисах, для которых прокси терминирует TLS; «соседи» без переопределения останутся на http. info.scheme у самого прокси по-прежнему влияет только на его собственную строку в dwe info и не передаётся приложениям, маршрутизируемым через него.

hosts: всегда карта от имени хоста к имени хоста. Симметрично с ports. Для одного имени хоста принято использовать web.

workspace/services/main/service.yml
type: app
hosts:
web: app.localhost

Значения хостов определяются в workspace/services/<name>/service.yml; оверлеи workspace/local.yml могут переназначать отдельные записи — см. поведение глубокого слияния в Поведение загрузки.

Необязательный эмодзи или символ Unicode, отображаемый рядом с именем сервиса в дашборде dwe info при рендеринге блоков auto-urls.

workspace/services/main/service.yml
type: app
icon: "📦"

Если опущен, используется значение по умолчанию на основе типа:

typeИконка по умолчанию
app📦
tool🔧
infra🧱

Иконки рассматриваются как непрозрачный пользовательский контент — эмодзи, соединённые ZWJ (глифы семей, модификаторы профессий, вариации цвета кожи), поддерживаются, но длина не валидируется. Иконка появляется только в выводе dwe info; в других местах не используется.

⚠️ Избегайте эмодзи с Emoji_Presentation=No. Кодовые точки вроде 🛢 (U+1F6E2), 🗄 (U+1F5C4) и ⚙ (U+2699) являются «text-default» — они рендерятся как цветной эмодзи только за VS-16 (U+FE0F), и многие комбинации терминалов и шрифтов на macOS и Linux игнорируют этот хинт и отрисовывают их в 1 ячейке вместо 2. Lipgloss измеряет их как 2 ячейки, поэтому таблицы status / info недозаполняются и каждая колонка справа от иконки сдвигается.

dwe validate помечает такие иконки (warning, область config.icons) и предлагает безопасные замены из подобранной карты. Во время рендера runtime полностью отбрасывает неоднозначные иконки, а не позволяет им сломать выравнивание колонок — они просто не появятся в дашборде, таблице status или меню переключения. Та же оговорка применяется к иконкам в info.paths[].icon и пользовательским иконкам auto-hosts / auto-urls в workspace/info.yml.

Предпочитайте кодовые точки, являющиеся эмодзи по умолчанию — например, 📦, 🧱, 🐳, 📚, 💾, 🔧, 🧰 — или придерживайтесь однотиповых ASCII / символов псевдографики.

Необязательные метаданные для рендеринга этого сервиса в дашборде dwe info.

workspace/services/main/service.yml
type: app
info:
title: "Main Application"
primary_host: web
primary_port: http
paths:
- name: "API Documentation"
path: /api/docs
icon: "📖"
- name: "Profiler"
path: /?SPX_KEY=dev
icon: "⚡"
ПолеТипПо умолчаниюОписание
titlestringtitle-case(имя-папки)Отображаемое имя сервиса в дашборде (например, "Main Application"). Заменяет значение по умолчанию, выведенное из имени папки.
primary_hoststringwebКакой ключ из hosts показать в основной строке URL (например, console для сервиса с несколькими хостами).
primary_portstringhttpКакой ключ из ports показать в основной строке URL (например, console для сервиса с несколькими портами).
schemestring—Переопределение схемы URL уровня сервиса ("http" или "https"). Побеждает глобальную runtime.use_https, уступает per-port scheme: в развёрнутой форме ports.
pathslist—Упорядоченный список подпутей под основным URL. См. Записи info.paths ниже.

Когда использовать info.scheme. Установите, если сервис говорит по одной фиксированной схеме, отличной от дефолта проекта — например, dev-сервер Vite с @vitejs/plugin-basic-ssl слушает https://localhost:5173, а остальная часть проекта остаётся на http:// (OAuth-колбэки, обычный dev-бэкенд). Пустой info.scheme означает фолбэк на runtime.use_https, что корректно для проектов с единообразной HTTP- или HTTPS-конфигурацией.

Каждая запись в списке paths объявляет именованный подпуть относительно основного URL сервиса.

paths:
- name: "API Documentation"
path: /api/docs
icon: "📖"
- name: "Profiler"
path: /?SPX_KEY=dev
ПолеТипОбязательноОписание
namestringдаОтображаемое имя пути (например, "API Documentation"). Должно быть непустым и уникальным в списке paths сервиса.
pathstringдаПуть URL относительно основного хоста сервиса (должен начинаться с /). Пример: /api/docs, /admin, /?SPX_KEY=dev.
iconstringнетНеобязательный эмодзи или символ, добавляемый перед именем пути. По умолчанию 🔗, если опущен.

Сервисы без блока info всё равно включаются в блоки дашборда auto-urls (если их типы include совпадают) и рендерят свой основной URL; они просто не добавляют пользовательский заголовок или подпути.

⚠️ Устарело. Механизм копирования configs: (и подполе mountpoint) вытеснен render.config + блоком generated. Он продолжает работать, но dwe validate выдаёт предупреждение, и при каждом шаге копирования один раз срабатывает runtime-уведомление об устаревании. Новым проектам следует рендерить конфиги из шаблонных паков; см. render config для миграции.

Перечисляет конфигурационные файлы, копируемые в hub сервиса при deploy.

configs:
- .env # сокращение: копирует src в configs/.env, монтирует по умолчанию
- file: .env
mountpoint: src/.env # явное место назначения внутри контейнера
ПолеОписание
file (или сокращение-строка)Имя исходного файла (относительно configs/services/<service>/)
mountpoint⚠️ Устарело. Путь относительно dir сервиса (например, src/.env), где файл «трогается» после копирования. Используется билтином service_configs_copy для создания заглушки под вложенные файловые bind-монтирования Docker Desktop virtiofs. Необязательно.

Исходный каталог configs/services/<service>/ принадлежит проекту и коммитится в git; целевой services/<service>/configs/ создаётся при deploy и попадает в gitignore.

Дополнительные каталоги для создания внутри hub-каталога сервиса помимо обязательного src.

dirs:
- logs
- home
- runtime
  • Пути относительны dir сервиса (например, ./services/main/logs).
  • Каталог src/ всегда создаётся и здесь не перечисляется; он также защищён (skip-семантика) в режиме recreate, поэтому исходный код никогда не стирается.
  • Каталог configs/ не обязателен — он создаётся лениво билтином service_configs_copy, когда объявлен блок configs:. Если он нужен заранее или должен стираться под recreate, перечислите его явно здесь.
  • Когда сервис extends другой, поле dirs потомка дописывается к родительскому (дедуплицировано, сначала родитель).
  • Используется билтином service_dirs_ensure во время deploy.

Управляет тем, как dwe shell и выполнение CLI ведут себя для этого сервиса.

cli:
mode: auto # auto | exec | run
shell: bash
user: www-data
workdir: /workspace/src
env:
- XDEBUG_CONFIG="cli_color=1"
ПолеПо умолчаниюОписание
modeautoauto = exec при работающем контейнере, run при отсутствии, ошибка при остановленном; exec = всегда docker exec (ошибка, если не запущен); run = всегда docker compose run --rm
shellbashБинарь shell для вызова внутри контейнера
userтекущий UIDПользователь для запуска внутри контейнера
workdirwork_dir_internal (затем dir_internal)Рабочий каталог shell-сессии
env—Дополнительные env-переменные, внедряемые в shell-сессию

Флаги CLI переопределяют конфиг cli. Порядок приоритета (от высшего к низшему): флаги --root/--user/--shell/--env → конфиг cli → встроенные значения по умолчанию.

cli.env принимает либо YAML-карту, либо список вида KEY=VALUE; обе формы дают одну и ту же внутреннюю карту и взаимозаменяемы.

# Форма карты
cli:
env:
XDEBUG_CONFIG: "cli_color=1"
PHP_IDE_CONFIG: serverName=dwe
# Форма списка
cli:
env:
- XDEBUG_CONFIG="cli_color=1"
- PHP_IDE_CONFIG=serverName=dwe

Форма списка удобна при копировании из файла .env; форма карты дружелюбнее для наследования и переопределения отдельных ключей через extends:.

Необязательный список пользовательских колонок, дописываемых к таблице status, специфичной для типа — dwe status apps для type: app, dwe status tools для type: tool, dwe status infra для type: infra (и составной dwe status по умолчанию). Каждая запись объявляет имя колонки и герметичный Go-шаблон, вычисляемый построчно против смерженного конфига.

workspace/services/main/service.yml
type: app
container: app-main
dir: ./services/main
status:
- name: CONTAINER
value: "{{ .ServiceCfg.Container }}"
- name: TAG
value: "{{ .Globals.baseImageTag }}"
ПолеТипОбязательноОписание
namestringдаЗаголовок колонки (приводится к верхнему регистру в отрендеренной таблице).
valuestringдаGo-шаблон, вычисляемый через tpl.Render. Герметичный — без доступа к env / FS / сети.

Контракт данных шаблона (Go-шаблоны чувствительны к регистру):

ПутьИсточникРегистр
.ServiceCfg.<Field>типизированный ServiceConfig для сервиса этой строкиPascalCase имена полей Go (.ServiceCfg.Container, .ServiceCfg.Dir)
.Globals.<key>cfg.Raw["globals"], если есть, иначе nilстрочные YAML-ключи (.Globals.baseImageTag)
.Raw.<key>...полная карта cfg.Rawстрочные YAML-ключи (.Raw.services.main.ports.http, .Raw.project.name)

В корне данных только эти три ключа — нет алиасов .Project / .Runtime в корне. Углубляйтесь в .Raw.project.*, .Raw.runtime.*, .Raw.services.*.

Обработка ошибок: шаблон, вызвавший ошибку, рендерится в таблице как — и вносит вклад в одно агрегированное предупреждение (warning: N custom status expression(s) failed to render) в stderr. Команда всё равно завершается с кодом 0.

Порядок колонок: в пределах одного раздела status по типу (apps / tools / infra), когда несколько сервисов объявляют пересекающиеся колонки, порядок колонок в отрендеренной таблице — «первое появление» при детерминированной алфавитной итерации по сервисам этого типа. Сервисы, объявляющие меньше колонок, оставляют отсутствующие ячейки как —.

Вложенный блок, управляющий тем, рендерит ли (и как именно) система файлы для этого сервиса из шаблонных паков. Содержит четыре подблока: ide, ai, git (у каждого одинаковая структура enabled / template) и config (один пин template — см. Блок render.config).

Управляет тем, рендерит ли (и как именно) IDE-рендер конфигурационные файлы для этого сервиса из шаблонных паков.

render:
ide:
enabled: true # opt-in для IDE-рендера для этого сервиса
template: main-debug # использовать пользовательский шаблонный пак
ПолеПо умолчаниюОписание
enabledtrue для type: app; false иначеВключить этот сервис в IDE-рендер. dwe render ide уважает эту настройку; см. Активация ниже.
template—Необязательное имя каталога пользовательского шаблонного пака. Должно быть единственным ключом каталога под workspace/templates/ide/ (без разделителей пути, без .., без абсолютных путей, без ведущей .). Если опущен, рендер откатывается к пакам, специфичным для имени сервиса, затем к глобальным. Явные паки строгие: опечатка приведёт к ошибке, а не к молчаливому фоллбэку.

IDE-рендер требует одновременно активации и условий политики:

  1. Активация проекта: Сервис должен быть включён на уровне проекта (через трёхслойное слияние конфига; обязательные сервисы всегда включены).
  2. Политика IDE: Настройка render.ide.enabled должна быть true.

Сервис рендерится только если оба условия выполнены. Отключение любого подавляет рендер.

Политика по умолчанию: сервисы type: app по умолчанию имеют render.ide.enabled: true (opt-out); tool / infra вообще не могут нести блок render: (разрешённые поля по типу). Чтобы рендерить файлы IDE для не-app сервиса, его нужно переопределить по типу — нет per-pack обходного пути.

dwe render ide ищет шаблонные паки в таком порядке; используется первое совпадение:

  1. workspace/templates/ide/<template>/ (если template задан) — строго: пак должен существовать, иначе рендер падает
  2. workspace/templates/ide/<service-name>/ (если template не задан)
  3. workspace/templates/ide/default/ (финальный фоллбэк)

Когда указан явный template:, а пак не найден, рендер падает с ошибкой (ловит опечатки). Когда явный шаблон не задан и неявная цепочка исчерпывается без нахождения пака, рендер пропускается с предупреждением.

После выбора пака команда читает manifest.yml пака, чтобы определить, какие файлы рендерить и какие симлинки создавать. Манифест объявляет:

  • render: исходные файлы шаблонов (должны оканчиваться на .tmpl) и их пути назначения
  • symlinks: относительные симлинки для создания внутри каталога сервиса (необязательно)

Все назначения относительны каталога сервиса (например, services/main/). Вложенные пути допустимы (например, .devcontainer/devcontainer.json).

Эта основанная на манифесте модель позволяет добавлять поддержку любого IDE или инструмента (.cursor/, .zed/, .envrc и т.д.) без модификации кода — добавьте файлы шаблонов и объявите их в manifest.yml.

Когда несколько сервисов разделяют один dir (например, main и main-debug, оба указывающие на ./services/main), только самый дочерний (глубже всего в цепочке extends) сервис рендерит файлы IDE. Остальные отчитываются как пропущенные с предупреждением о коллизии.

Явная позиционная форма dwe render ide <service> трактует аргумент как якорь hub: он валидируется как реальный сервис, но затем разрешается через ту же политику коллизий. Поэтому dwe render ide main на самом деле рендерит main-debug, когда main-debug включён — полезно из per-service пайплайнов deploy, передающих каноническое имя сервиса и ожидающих результат с учётом варианта.

workspace/services/main/service.yml
type: app
dir: ./services/main
# render.ide.enabled по умолчанию true
workspace/services/main-debug/service.yml
type: app
extends: main # тот же dir, что у родителя
dir: ./services/main
render:
ide:
template: main-debug # использовать другой шаблонный пак
# файлы IDE идут в ./services/main/ с содержимым из пака main-debug
# (main-debug побеждает, потому что extends main)

В этом примере dwe render ide создаёт файлы в ./services/main/ с использованием шаблонного пака main-debug и выдаёт предупреждение, что main пропущен из-за коллизии.

Разбор примера: раскладка шаблонного пака

Заголовок раздела «Разбор примера: раскладка шаблонного пака»

Этот пример показывает, как организованы шаблонные паки и какие файлы получаются в каталоге сервиса.

Структура проекта:

workspace/services/
main/
service.yml
main-debug/
service.yml
workspace/templates/ide/
default/
manifest.yml
.devcontainer/devcontainer.json.tmpl
.vscode/settings.json.tmpl
main-debug/
manifest.yml
.devcontainer/devcontainer.json.tmpl
.vscode/settings.json.tmpl
.vscode/launch.json.tmpl

Определения сервисов:

workspace/services/main/service.yml
type: app
dir: ./services/main
# render.ide.enabled по умолчанию true; рендерит, используя пак default
workspace/services/main-debug/service.yml
type: app
extends: main
container: app-main-debug
dir: ./services/main
render:
ide:
template: main-debug # переопределить на использование пака main-debug

После dwe render ide:

services/main/
.devcontainer/
devcontainer.json ← отрендерено из main-debug/.devcontainer/devcontainer.json.tmpl
.vscode/
settings.json ← отрендерено из main-debug/.vscode/settings.json.tmpl
launch.json ← отрендерено из main-debug/.vscode/launch.json.tmpl

Обратите внимание, что main пропущен из-за коллизии (тот же dir, что и у main-debug), поэтому рендерится только шаблонный пак main-debug.

Управляет тем, рендерит ли (и как именно) агентная документация hub-уровень документации для этого сервиса из шаблонных паков.

render:
ai:
enabled: true # opt-in для рендера агент-документации для этого сервиса
template: custom-docs # использовать пользовательский шаблонный пак
ПолеПо умолчаниюОписание
enabledtrue для type: app; false иначеВключить этот сервис в вывод dwe render ai. Когда true, в hub сервиса генерируется агент-ориентированная документация.
template—Необязательное имя каталога пользовательского шаблонного пака. Должно быть единственным ключом каталога под workspace/templates/ai/ (без разделителей пути, без .., без абсолютных путей, без ведущей .). Если опущен, рендер откатывается к пакам, специфичным для имени сервиса, затем к default. Явные паки строгие: опечатка приведёт к ошибке, а не к молчаливому фоллбэку.

Рендер агент-документации требует одновременно активации и условий политики:

  1. Активация проекта: Сервис должен быть включён на уровне проекта (через трёхслойное слияние конфига; обязательные сервисы всегда включены).
  2. Политика агент-документации: Настройка render.ai.enabled должна быть true.

Сервис рендерится только если оба условия выполнены. Отключение любого подавляет рендер.

Политика по умолчанию: сервисы type: app по умолчанию имеют render.ai.enabled: true (opt-out); не-app сервисы по умолчанию имеют false и должны подключаться явно (установите render.ai.enabled: true). Установите render.ai.enabled: false, чтобы подавить генерацию агент-документации для app-сервиса.

dwe render ai ищет шаблонные паки в таком порядке; используется первое совпадение:

  1. workspace/templates/ai/<template>/ (если template задан) — строго: пак должен существовать, иначе рендер падает
  2. workspace/templates/ai/<service-name>/ (если template не задан)
  3. workspace/templates/ai/default/ (финальный фоллбэк)

Когда указан явный template:, а пак не найден, рендер падает с ошибкой (ловит опечатки). Когда явный шаблон не задан и неявная цепочка исчерпывается без нахождения пака, рендер пропускается с предупреждением.

После выбора пака команда читает manifest.yml пака, чтобы определить, какие файлы рендерить и какие симлинки создавать. Манифест объявляет:

  • render: исходные файлы шаблонов (должны оканчиваться на .tmpl) и их пути назначения
  • symlinks: относительные симлинки для создания внутри hub сервиса (должны ссылаться на выводы из render)

Все назначения относительны hub-каталога сервиса (например, services/main/). Вложенные пути допустимы (например, .github/copilot-instructions.md).

Когда несколько сервисов разделяют один dir (например, main и main-debug, оба указывающие на ./services/main), только канонический владелец hub — наименее дочерний сервис (мельче всего в цепочке extends) — рендерит агент-документацию. Обоснование: агент-документация описывает идентичность hub, и когда потомок extends родителя и разделяет его dir, родитель владеет hub; потомок — runtime-вариант того же воркспейса. Проигравшие варианты отчитываются как пропущенные с предупреждением о коллизии.

Явная позиционная форма dwe render ai <service> трактует аргумент как якорь hub (так же, как render ide): аргумент валидируется как реальный сервис, затем разрешается через политику коллизий. Поэтому dwe render ai main-debug всё равно рендерит main, когда оба включены — вариант разрешается к каноническому владельцу hub.

(Примечание: это отличается от dwe render ide, где побеждает самая глубокая цепочка extends, потому что конфиги IDE — про per-variant переопределения.)

Разбор примера: раскладка шаблонного пака

Заголовок раздела «Разбор примера: раскладка шаблонного пака»

Этот пример показывает, как организованы шаблонные паки агента и какие файлы получаются в каталоге сервиса.

Структура проекта:

workspace/services/
main/
service.yml
workspace/templates/ai/
default/
manifest.yml
AGENTS.md.tmpl
.github/copilot-instructions.md.tmpl

Манифест (workspace/templates/ai/default/manifest.yml):

render:
- from: AGENTS.md.tmpl
to: AGENTS.md
- from: .github/copilot-instructions.md.tmpl
to: .github/copilot-instructions.md
symlinks:
- link: GEMINI.md
to: AGENTS.md

Пример шаблона (AGENTS.md.tmpl):

# {{.Service}} Service Hub
This is the {{.Service}} service running inside a DWE-managed hub.
The application source code is at `src/`.
Service container: {{.ServiceCfg.Container}}
Workspace root: {{.ServiceCfg.DirInternal}}

Определение сервиса (workspace/services/main/service.yml):

type: app
dir: ./services/main
# render.ai.enabled по умолчанию true; рендерит, используя пак default

После dwe render ai:

services/main/
AGENTS.md ← отрендерено из AGENTS.md.tmpl
GEMINI.md ← симлинк на AGENTS.md
.github/
copilot-instructions.md ← отрендерено из .github/copilot-instructions.md.tmpl

Управляет тем, рендерятся ли (и как именно) shell git-хуки в каталог src/.git/hooks/ сервиса из шаблонных паков.

render:
git:
enabled: true # opt-in для рендера git-хуков для этого сервиса
template: custom-hooks # использовать пользовательский шаблонный пак
ПолеПо умолчаниюОписание
enabledtrue для type: app; false иначеВключить этот сервис в вывод dwe render git. Отражает политику по умолчанию render.ide.
template—Необязательное имя каталога пользовательского шаблонного пака. Должно быть единственным ключом каталога под workspace/templates/git/ (без разделителей пути, без .., без абсолютных путей, без ведущей .). Если опущен, рендер откатывается к пакам, специфичным для имени сервиса, затем к default. Явные паки строгие: опечатка приведёт к ошибке, а не к молчаливому фоллбэку.

Наследование extends для render.git.enabled и render.git.template следует тем же правилам, что render.ide и render.ai: явные значения потомка переопределяют родительские; опущенные значения наследуются. Разрешение коллизий на разделяемом dir использует deepest-extends-wins (так же, как render.ide).

Хуки записываются в <svc.Dir>/src/.git/hooks/<basename> с режимом 0755. Сервисы, у которых отсутствует src/.git (нет git-чекаута) или это файл (указатель worktree/submodule), пропускаются с предупреждением. См. render git для полного справочника, схемы манифеста и примеров.

Управляет рендером конфигурационных файлов для этого сервиса — основанный на рендере преемник устаревшего механизма копирования configs:. Конфиги становятся чистыми выводами рендера из шаблонного пака, записываемыми прямо в дерево hub сервиса (обычно src/..., уже примонтированное как dir), переигрывая любые собранные значения generated.

render:
config:
template: laravel # необязательный пин пака; иначе соглашение + .local
ПолеПо умолчаниюОписание
template—Необязательное имя каталога пользовательского шаблонного пака под workspace/templates/config/<template>/. Когда задан, разрешение строгое (опечатка приводит к ошибке, а не к молчаливому фоллбэку). Когда опущен, разрешение идёт по цепочке имя сервиса → предки по extends → default, плюс соседнее переопределение <pack>.local/.

В отличие от render.ide / ai / git, у render.config нет флага enabled — рендер конфигов гейтится исключительно тем, разрешается ли пак (opt-in: нет пака → нет рендера). Шаблоны конфигов используют сокращение ${...} (например, ${services.main.ports.http}, ${vars.databases.main}, ${generated.app_key}) — намеренное расхождение с сырым субстратом {{ }}, используемым другими видами рендера. См. render config для полного справочника, субстрата, схемы манифеста и потока harvest/replay.

Объявляет per-service значения, генерируемые сервисом (Laravel APP_KEY, Magento crypt.key, …), которые DWE собирает обратно из собственного выходного файла сервиса в долговременное хранилище (.dwe/generated.yml) и переигрывает при каждом последующем рендере через пространство имён ${generated.<name>}. Модель — harvest, а не mint: значение генерирует сам сервис (например, php artisan key:generate); DWE лишь считывает его обратно как строку.

workspace/services/main/service.yml
type: app
dir: ./services/main
generated:
app_key:
file: src/.env # выходной файл, относительно hub сервиса (svc.Dir)
pattern: '^APP_KEY=(.*)$' # regex; группа захвата 1 = значение
ПолеТипОбязательноОписание
filestringдаВыходной файл, в который сервис записывает значение, относительно hub-каталога сервиса (svc.Dir). Должен быть содержащимся относительным путём (без ..).
patternstringдаRegex, применяемый построчно; группа захвата 1 — собираемое значение. Должен компилироваться и объявлять ≥1 группу захвата.

Ключ карты (app_key) — это идентификатор ${generated.<name>}. dwe validate отклоняет невалидный regex, отсутствие группы захвата, выходящий за пределы путь file или имя поля, которое не является валидным идентификатором ${generated.<name>}.

Шаг генерации сервиса обычно гейтится предикатом generated-missing <svc> <field>, так что он выполняется только при первом deploy (когда значение ещё не собрано), а затем собирается билтином service_generated_harvest. Значение переживает run / передеплой и сохраняется при reset, если не передан --clear-generated. См. render config для полного потока deploy, схемы хранилища и бутстрапа уже закоммиченного секрета через dwe render config <svc> --harvest.

Подключает сервис к host-мосту — DWE монтирует небольшой статический shim dwe в контейнер, чтобы команды dwe (git-хуки, команды проекта, read-only диагностика) выполнялись изнутри контейнера, перенаправляясь на host-демона. Мост выключен по умолчанию для всех типов сервисов — включайте явно через enabled: true.

workspace/services/main/service.yml
type: app
dir: ./services/main
bridge:
enabled: true # по умолчанию: false — мост строго opt-in
# shim_path: /usr/local/bin/dwe # переопределение точки монтирования (коллизия base-образа)
# on_unreachable: fail # fail | warn — политика shim, когда демон недоступен
ПолеТипОбязательноПо умолчаниюОписание
enabledbool (tristate)нетfalse (для всех типов)Внедрить shim-бинарник и bridge-монтирования в контейнер этого сервиса.
shim_pathstringнет/usr/local/bin/dweАбсолютный путь в контейнере, куда монтируется shim; переопределите, если base-образ уже содержит файл там.
on_unreachablestringнетfailfail — shim печатает ошибку и выходит с кодом 1, когда host-демон недоступен (хук блокирует коммит); warn — печатает предупреждение и выходит с кодом 0.

bridge.enabled — это tristate, наследуемый через extends: сервиса так же, как render.git.enabled: явное значение в потомке выигрывает, неустановленный потомок наследует родителя, а выключенное значение по умолчанию применяется, только если ни один из них не задан. shim_path и on_unreachable наследуются, когда потомок оставляет их пустыми. Сервис type: app с включённым мостом должен объявлять пару dir / dir_internal — поверх неё работает трансляция рабочей директории shim, и dwe validate (домен bridge) предупреждает, когда её нет. См. Host-мост для транспортов, in-container политики команд, генерируемого compose-оверлея и жизненного цикла демона.