Конфигурация сервиса (workspace/services/<name>/service.yml)
Объявления сервисов DWE-проекта.
Содержание
Заголовок раздела «Содержание»- Назначение
- Типы сервисов
- Разрешённые поля по типам
- Поведение загрузки
- Структура
- Страницы
- Связанные команды
Назначение
Заголовок раздела «Назначение»Определения сервисов располагаются в workspace/services/, по одной папке на сервис. Каждый сервис объявляется в workspace/services/<name>/service.yml, где <name> становится ключом сервиса в итоговой карте DweConfig.Services. Каждая запись объявляет имя контейнера, порты, хосты, оверлей compose и необязательные структурные поля. Дискриминатор type: определяет, какие поля допустимы для записи.
При загрузке перечисляется каждая подпапка workspace/services/*/, и её service.yml парсится в строгом режиме: файл обязан декларировать type, каждое поле сверяется со списком разрешённых для этого типа, extends: отвергается у сервисов не-app, проверяются формы ports / hosts. Затем межсервисные цепочки extends: разрешаются в топологическом порядке, а поля родителей сливаются в потомков. Отсутствующий каталог workspace/services/ даёт пустой набор сервисов (не ошибка). Переключатели на стороне разработчика (enabled:, ports:, hosts:) лежат в трёхслойном оверлее; структурные поля принадлежат исключительно workspace/services/<name>/service.yml.
Типы сервисов
Заголовок раздела «Типы сервисов»Каждая запись в services: требует ключа type:. Поддерживаются три значения:
type: | Семантика | Жизненный цикл deploy | Цель depends_on: | Поля только для app |
|---|---|---|---|---|
app | Сервис с исходным кодом в dir:; рендерит шаблоны IDE/AI/git; запускается через dwe deploy. | да (может существовать workspace/services/<name>/deploy.yml) | да | да (dir, dir_internal, work_dir_internal, configs, dirs, extends, cli, render, generated) |
tool | Эфемерный служебный контейнер (adminer, mailpit, redis-insight). Не может быть целью зависимости ни одного сервиса. | нет | нет | нет |
infra | Поддерживающий сервис (БД, кеш, очередь, поиск). Может быть целью зависимости для app / других infra. | нет | да | нет |
Фиксированные правила:
extends:только для app. Записьtool/infraсextends:отклоняется при загрузке.depends_on:не может ссылаться на записьtype: tool. Это проверяется при загрузке (ErrDependsOnTool), а не только при валидации.workspace/services/<name>/deploy.ymlподдерживается для любого типа сервиса (app, tool, infra). Полный deploy (dwe deploy run) перечисляет каждый включённый сервис, у которого естьdeploy.yml;dwe deploy run --service <name>работает для любого типа сервиса с deploy-файлом независимо от состояния включения.ports:всегдаmap[string]int, аhosts:всегдаmap[string]string. Скалярных сокращенийport:/host:нет. Сервис с одним портом записывается какports: { http: 8025 }.- Семантика типов разделяет порядок эмиссии файлов
docker composeнаtool → infra → app.
Сервисы type: infra могут быть необязательными (required: false) — они принимают оверлей compose: и переключаются через dwe services enable|disable <name> так же, как app и tool. Обязательная infra (required: true, типична для поддерживающих сервисов вроде баз данных, кешей и очередей) всегда включена и не переключается. Необязательная infra семантически подходит для компонентов на пути запроса или данных, не строго обязательных каждому разработчику (например, Varnish-кеш перед nginx или MinIO как S3-хранилище, используемое только когда внешний S3 не настроен).
Зачем нужна матрица типов
Заголовок раздела «Зачем нужна матрица типов»Разрешённые поля по типам отражают разные роли сервисов в dev-окружении:
app: отвечает за исходный код и запущенный контейнер. Поддерживает монтирования, шаблоны и оркестрацию деплоя (dir,extends,configs,depends_on).tool: автономный служебный контейнер (UI базы данных, фронтенд наблюдаемости и т.д.). Не отвечает за исходный код; не может зависеть от других сервисов и быть цельюdepends_on.infra: поддерживающий контейнер (база данных, кеш, брокер, обратный прокси). Может быть цельюdepends_on, но не отвечает за исходный код; более лёгкий футпринт, чем app.
Это разделение гарантирует, что логика сборки и деплоя явная (определена только в type: app), а инфраструктурные сервисы остаются независимо тестируемыми без зависимости от кода приложения.
Разрешённые поля по типам
Заголовок раздела «Разрешённые поля по типам»| Поле | app | tool | infra |
|---|---|---|---|
type | ✓ | ✓ | ✓ |
container | ✓ | ✓ | ✓ |
required | ✓ | ✓ | ✓ |
compose | ✓ | ✓ | ✓ |
ports | ✓ | ✓ | ✓ |
hosts | ✓ | ✓ | ✓ |
icon | ✓ | ✓ | ✓ |
info | ✓ | ✓ | ✓ |
depends_on | ✓ | — | ✓ |
status | ✓ | ✓ | ✓ |
dir | ✓ | — | — |
dir_internal | ✓ | — | — |
work_dir_internal | ✓ | — | — |
configs | ✓ | — | — |
dirs | ✓ | — | — |
extends | ✓ | — | — |
cli | ✓ | — | — |
render | ✓ | — | — |
generated | ✓ | — | — |
on_enable | ✓ | ✓ | ✓ |
on_disable | ✓ | ✓ | ✓ |
notes | ✓ | ✓ | ✓ |
bridge | ✓ | ✓ | ✓ |
Запрещённое поле — фатальная ошибка загрузки (ErrServiceFieldNotAllowed). Валидация агрегирует нарушения каждого файла через errors.Join, поэтому один проход разбора выявляет сразу все проблемы.
Поведение загрузки
Заголовок раздела «Поведение загрузки»- Каждый
workspace/services/<name>/service.ymlдекодируется строго — неизвестные и не разрешённые для данного типа поля являются фатальными ошибками. Ошибки со всех папок сообщаются вместе, поэтому сразу выявляются все сломанные папки, а не только первая. - Наследование сервисов через
extends:разрешается в топологическом порядке (родители раньше потомков), поэтому многоуровневые цепочки (C → B → A) сливаются корректно независимо от порядка обхода карты. Циклы и неизвестные родители сообщаются как ошибки загрузки.extends:только для app. - Для каждого потомка от родителя наследуются только поля с нулевым значением; при конфликтах поля потомка имеют приоритет. Наследуемые слайсы и карты копируются защитно, поэтому мутация потомка никогда не повреждает родителя.
- Поле
dirsдедуплицируется между родителем и потомком (сначала родитель, потомок дописывается).cli.envсливается рекурсивно: родитель даёт значения по умолчанию, потомок выигрывает при конфликтах ключей. - После загрузки
enabledвычисляется из трёхслойного слияния (services.<name>.enabled); обязательные сервисы принудительно получаютenabled: true. - Оверлеи под
services.<name>могут задавать толькоenabled:,ports:иhosts:. Любое другое поле там — ошибка оверлея с учётом слоя; структурные поля (container,dir,configs,compose,extends, …) принадлежатworkspace/services/<name>/service.yml. Валидатор оверлея также проверяет форму:ports:должна быть картой имя → целое число в1..65535;hosts:— картой имя → строка. ports:иhosts:глубоко сливаются по имени записи поверх объявленной карты: пер-разработческое переопределение вworkspace/local.ymlзатрагивает только перечисленные ключи; объявленные записи, не упомянутые в оверлее, сохраняются. Через оверлей можно также вводить новые записи. Это первоклассная возможность DWE: разработчикам регулярно нужно переназначить порт, конфликтующий с уже занятым на их хосте, или сменить*.localимя хоста, не редактируя общийworkspace/services/<name>/service.yml.- Каждый разрешённый сервис (включая вложенные карты
ports/hostsпосле оверлея) внедряется вDweConfig.Raw["services"], так что dot-пути вродеservices.main.ports.httpиservices.adminer.hosts.webразрешаются в правилах экспорта, шаблонахdocker.yml,default_from:команд и ссылкахinfo.yml. - Значения портов ограничены
1..65535во время загрузки (как вservice.yml, так и в слоях оверлея).
Пример: разработчик, у которого хост уже занимает 8027, локально переназначает adminer без правки общей конфигурации —
# workspace/local.yml (не отслеживается git)services: adminer: ports: http: 9027 # переопределяет объявленный 8027 main: hosts: api: api.dev.local # добавляет новую запись; web остаётся как объявленоСтруктура
Заголовок раздела «Структура»Каждый сервис лежит в собственной папке в workspace/services/. Имя папки становится ключом сервиса.
workspace/services/ main/ service.yml deploy.yml # необязательно; включает жизненный цикл deploy для этого сервиса db/ service.yml varnish/ service.yml adminer/ service.yml# type: app — владеет исходным кодом в dir:, имеет жизненный цикл deploy, рендерит шаблоныtype: appcontainer: app-mainrequired: truedir: ./services/maindir_internal: /workspacework_dir_internal: /workspace/srcextends: <parent-app-key> # только для appdepends_on: [db, redis] # может указывать на app или infra (никогда tool)icon: "📦"hosts: web: app.localhostports: http: 80info: title: "Main Application" primary_host: web primary_port: http paths: - name: "API Documentation" path: /api/docs icon: "📖"compose: - compose/services/main/overlay.ymlconfigs: - file: .env mountpoint: src/.envdirs: [logs, home, runtime]cli: mode: auto|exec|run shell: bash user: www-data workdir: /workspace/src env: - KEY=valuerender: ide: { enabled: true, template: <pack> } ai: { enabled: true, template: <pack> } git: { enabled: true, template: <pack> }# type: infra — поддерживающий сервис, может быть целью depends_ontype: infracontainer: dbrequired: true # всегда включённый поддерживающий сервисports: mysql: 13306# type: infra (необязательный) — переключается через `dwe services enable varnish`# Примечание: поле container здесь опущено — по умолчанию используется имя папки "varnish"type: infracompose: - compose/services/varnish/overlay.ymlports: http: 6081# type: tool — эфемерный служебный контейнер, никогда не цель depends_ontype: toolcontainer: adminericon: "🔧"compose: - compose/tools/adminer.ymlports: http: 8027hosts: web: db.localhostinfo: title: AdminerСтраницы
Заголовок раздела «Страницы»- Справочник полей — каждое поле верхнего уровня плюс блоки
ports,hosts,icon,info,configs,dirs,cli,statusиrender - Наследование через
extends— топологическая сортировка, правила разрешения, защита «только app», разобранный пример - Примеры и жизненный цикл переключения — полное определение сервиса,
on_enable/on_disable/notes, типичные ловушки
Связанные команды
Заголовок раздела «Связанные команды»dwe shell [service]— открыть shell в контейнере любого включённого сервиса (блок умолчанийcli:действует только дляtype: app; tool/infra используют встроенные умолчания bash/auto).dwe status— составное представление только для чтения: разделы apps + tools + infra, у каждого своиstatus:колонки.dwe status apps/dwe status tools/dwe status infra— таблицы по типам.dwe services— интерактивный мультивыбор для переключения каждого необязательного сервиса всех типов.dwe services list— read-only список всех настроенных сервисов (app, tool, infra, включая обязательную infra) с их состоянием enabled/running; то же представление, на которое голыйdwe servicesоткатывается при неинтерактивном stdin или под--output json. Никогда не пишет вlocal.ymlи не запускает lifecycle-хуки.dwe services enable <name>/dwe services disable <name>— переключение по имени (тип определяется внутри).dwe deploy run— запускает полный пайплайн deploy; перечисляет все включённые сервисы, у которых естьworkspace/services/<name>/deploy.yml(любой тип сервиса).dwe reset run --service <name>— сбрасывает один сервис: останавливает и удаляет контейнер, удаляетdir:сервиса, если он объявлен и существует, запускает per-servicereset.yml, если он есть, помечает сервис требующим последующего deploy. Тома автоматически не удаляются (opt-in черезdocker_remove_project_volumes).