dwe render env
Сгенерировать содержимое .env из объединённой конфигурации. По умолчанию вывод идёт в stdout; передайте --out <path>, чтобы записать в файл (родительские каталоги создаются).
Содержание
Заголовок раздела «Содержание»- Конвейер
- Системные переменные
- Правила экспорта
- Разрешение значения
- Истинность
- Зашифрованные значения и защита от маркера
- Формат вывода
- Проработанный пример
- Частые ловушки
- Связанные справочники
Конвейер
Заголовок раздела «Конвейер»render env не итерирует сервисы и не читает шаблоны с диска. Он проходит по упорядоченному списку правил экспорта в объединённой конфигурации и выводит по одной строке на правило.
flowchart TD
M["Объединённая конфигурация"] --> SYS["Вывести системные переменные<br/>PROJECT, UID, GID,<br/>COMPOSE_PROJECT_NAME"]
SYS --> R{"Для каждого правила в<br/>exports.env"}
R --> W["Вычислить when"]
W -- ложно --> R
W -- "истинно/отсутствует" --> V["Разрешить from<br/>через dot-path"]
V --> F["Отформатировать значение<br/>по подсказке format"]
F --> O["Записать строку<br/>NAME=value"]
O --> R
R -- "конец" --> OUT["stdout / файл"]
Список правил экспорта живёт под exports.env в workspace/defaults.yml (см. справочник по exports.env). Порядок правил в YAML задаёт порядок строк на выходе.
Системные переменные
Заголовок раздела «Системные переменные»Четыре переменные выводятся до любых правил, независимо от exports.env. Первые три есть всегда; COMPOSE_PROJECT_NAME выводится, когда разрешается в непустое значение:
| Переменная | Источник | Замечания |
|---|---|---|
PROJECT | project.name из workspace.yml | имя проекта как есть, включая верхний регистр. Используется Make-целями и именованием в рамках проекта |
UID | UID хоста — 1000 на macOS, реальный UID хоста на Linux/WSL | жёстко прибит к 1000 на macOS, потому что Docker Desktop крутит контейнеры в Linux-VM, где UID хоста не маппятся напрямую |
GID | GID хоста — та же платформенная логика, что и для UID | то же обоснование, что и у UID |
COMPOSE_PROJECT_NAME | имя compose-проекта, которое dwe передаёт в -p | project_name из workspace/docker.yml (или docker.local.yml), иначе <project.prefix>-<project.name>, всегда в нижнем регистре. Опускается, если разрешается в пустое значение — в этом случае dwe тоже не передаёт -p. Ошибка разрешения (сломанный ${...} в project_name) валит рендер |
COMPOSE_PROJECT_NAME — это не PROJECT. PROJECT — это сырое project.name; COMPOSE_PROJECT_NAME — разрешённое имя compose-проекта в нижнем регистре, которое docker.yml может переопределить целиком. Для project.name: cueBreaker с префиксом по умолчанию строки читаются как PROJECT=cueBreaker и COMPOSE_PROJECT_NAME=dwe-cuebreaker. Это то же значение, которое контракт команд type: shell экспортирует в окружение host-команды — один резолвер, два способа доставки.
Поскольку .env лежит в каталоге compose-проекта, «сырой» docker compose … из корня проекта теперь работает в том же проекте, что и dwe, поверх любого верхнего name: в вашем compose-файле — см. docker.md → project_name. Compose читает значение именно из этого файла, поэтому совпадение держится только из корня проекта и только пока .env актуален: без сгенерированного .env — или с файлом, записанным до смены project_name, — compose вернётся к собственному именованию и может выбрать другой проект. dwe run перегенерирует его; dwe render env пишет файл только с --out .env (без флага он печатает в stdout).
Имена PROJECT, UID, GID и COMPOSE_PROJECT_NAME зарезервированы: любое правило экспорта, пытающееся использовать одно из них как name, отвергается на этапе загрузки конфигурации с понятной ошибкой. Это касается любой команды, загружающей проектную конфигурацию, а не только dwe render env — поэтому опечатка ловится при первом же запуске после правки defaults.yml.
Правила экспорта
Заголовок раздела «Правила экспорта»Каждое правило сопоставляет dot-path в объединённой конфигурации имени env-переменной. Все значения по сервисам — enabled, container, ports.<port-name>, hosts.<host-name> — доступны под services.<name>.* независимо от типа (app / tool / infra).
exports: env: - name: APP_PORT from: services.main.ports.http format: int
- name: APP_HOST from: services.main.hosts.web
- name: TOOL_ADMINER_ENABLED from: services.adminer.enabled format: bool
- name: ADMINER_PORT from: services.adminer.ports.http format: int when: services.adminer.enabled
- name: ADMINER_HOST from: services.adminer.hosts.web when: services.adminer.enabled
- name: APP_URL from: runtime.urls.app default: http://localhost comment: Public application URLПоля правила
Заголовок раздела «Поля правила»| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
name | string | да | имя env-переменной, записываемой в .env |
from | string | да | dot-path, проходимый по объединённой конфигурации (например, services.main.ports.http, services.main.container) |
default | string | нет | fallback, когда from не разрешается или разрешается в ложную строку (см. Разрешение значения) |
required | bool | нет | если true, from отсутствует и default пустой — рендер падает с ошибкой |
format | string | нет | один из string (по умолчанию), bool, int — контролирует, как разрешённое значение выводится |
when | string | нет | dot-path; правило целиком пропускается, когда этот путь разрешается в ложное значение |
comment | string | нет | записывается как # comment строкой над переменной |
Порядок вычисления
Заголовок раздела «Порядок вычисления»Для каждого правила в исходном порядке:
- Гейт
when— если заданwhen, разрешить его dot-path в объединённой конфигурации. Если значение ложное — пропустить правило целиком (без строки и без комментария). - Разрешить
from— получить значение по dot-path. - Выбрать значение — см. Разрешение значения.
- Проверка required — если путь отсутствовал,
defaultне задан, аrequired: true, упасть с ошибкой, называющей отсутствующий путь. - Комментарий — если задан
comment, вывести# <comment>отдельной строкой. - Вывести — записать
<name>=<value>.
Нерезолвящиеся пути
Заголовок раздела «Нерезолвящиеся пути»Правило, чей from: не резолвится, без default: и без required: true, рендерится голым NAME= — пустым значением, которое доходит до каждого контейнера так, будто его так и объявили. dwe render env сообщает об этом — по одной строке на правило, в stderr, перед записью содержимого:
warning: exports.env[DB_PASSWORD]: from "vars.db.passwrod" does not resolve — rendered emptystderr оставляет dwe render env > .env побайтово прежним, а --output json подавляет предупреждение. Про две другие формы здесь не предупреждают: при default: отрендеренное значение — это собственный фолбэк автора, а required: true роняет рендер сразу. dwe validate репортит все три формы как предупреждения config.exports (см. validate.md), включая нерезолвящийся when:, который это предупреждение покрыть не может — такое правило пропускается и не рендерит строку вовсе.
Разрешение значения
Заголовок раздела «Разрешение значения»Выбранное значение зависит от трёх факторов: разрешился ли from, подсказки format и истинности разрешённого значения.
flowchart TD
A{"from разрешён?"} -- нет --> R{"required И<br/>default пустой?"}
R -- да --> ERR["ошибка"]
R -- нет --> D["использовать default"]
A -- да --> F{"format — bool<br/>или int?"}
F -- да --> USE["использовать разрешённое значение<br/>с применённым форматом"]
F -- нет --> T{"разрешённое значение истинно?"}
T -- да --> USE
T -- нет --> D
Асимметрия важна:
format: boolиformat: intвсегда используют разрешённое значение, даже если оноfalseили0. Это гарантирует, чтоTOOL_ADMINER=falseиPORT=0доедут до.env, а не молча сольются в default.format: string(по умолчанию) трактует ложные разрешённые значения как «толком не задано» и падает наdefault. Поэтому YAML-овая""вruntime.urls.appприведёт кdefault: http://localhost.
format формирует вывод:
| Format | Поведение |
|---|---|
string (по умолчанию) | привести разрешённое значение к строке как есть |
bool | булево значение выводится как литерал true или false. Прочие типы откатываются к обычной стрингификации |
int | разрешённое число выводится напрямую как строка (YAML-числа уже int-подобны) |
Истинность
Заголовок раздела «Истинность»Одно и то же правило истинности применяется и к when, и к string-format fallback:
| Значение | Истинно? |
|---|---|
| отсутствующий путь | нет |
false | нет |
0 (любой числовой тип) | нет |
"" | нет |
"false" | нет |
"0" | нет |
| что угодно ещё | да |
Пример: when: services.adminer.enabled пропускает правило всякий раз, когда сервис не задан, явно false или строка "false" / "0".
Замечание про синтаксис dot-path. Поля from: / when: правил экспорта используют голые dot-path в объединённую конфигурацию, а не синтаксис шаблонов {{ ... }}. Значения по сервисам лежат под services.<name>.* для каждого типа — например, from: services.adminer.ports.http, from: services.mailpit.hosts.web, from: services.main.container, when: services.adminer.enabled.
Зашифрованные значения и защита от маркера
Заголовок раздела «Зашифрованные значения и защита от маркера»Файл .env — единственный сток открытого текста, который читает контейнер; он в
gitignore и пишется с правами 0600 (существующий более свободный файл
ужимается при каждой записи). Поэтому правило вполне может экспортировать
зашифрованное значение vars.* — загрузчик конфига
расшифровывает его в памяти, так что from: vars.telegram.token выдаёт открытый
текст ровно как любое другое значение.
Когда значение не удаётся расшифровать на этой машине, render env
падает, вместо того чтобы опубликовать шифротекст как учётные данные:
exports.env[TELEGRAM_TOKEN]: value at vars.telegram.token is an undecrypted secret — see 'dwe secrets status'Проверка покрывает каждое выдаваемое значение — системные переменные
(PROJECT берётся из project.name, COMPOSE_PROJECT_NAME — из имени
compose-проекта) и каждое правило экспорта, включая
значение, пришедшее из default: правила, а не из from:. Защита живёт здесь, а
не в preflight, потому что ни один путь записи .env preflight не запускает:
dwe render env- автоматическая регенерация перед
dwe docker up/run/exec/restart/build dwe services enable/disable- рендер
.env, которыйdwe runделает до собственного preflight — поэтому на машине без ключа именно этоdwe runсообщает первым
Все четыре падают одинаково и называют, что делать. См.
secrets.md → Защита вывода.
Формат вывода
Заголовок раздела «Формат вывода»# Generated by dwe — do not edit manually
PROJECT=<project.name>UID=<UID хоста или 1000>GID=<GID хоста или 1000>COMPOSE_PROJECT_NAME=<имя compose-проекта в нижнем регистре># <comment из правила, если есть><NAME>=<value>...После шапки идёт пустая строка, затем системные переменные, затем правила.
Системный блок выводится в этом фиксированном порядке; COMPOSE_PROJECT_NAME
из него выпадает, если имя разрешается в пустое значение.
При указании --out <path>:
- Путь трактуется относительно текущего рабочего каталога, не корня проекта. Указывайте абсолютный путь, если нужно детерминированное расположение независимо от того, откуда вызвана команда (например, при запуске с
-c /path/to/workspace.ymlиз другого каталога). - Отсутствующие родительские каталоги создаются.
- Существующий файл заменяется полностью (без слияния, без сохранения комментариев).
Проработанный пример
Заголовок раздела «Проработанный пример»workspace/services/main/service.yml:
type: appcontainer: app-mainrequired: truedir: ./services/mainports: http: 8080workspace/services/adminer/service.yml:
type: toolcontainer: adminerports: http: 8027workspace/defaults.yml:
services: adminer: enabled: trueruntime: urls: app: ""exports: env: - name: APP_PORT from: services.main.ports.http format: int - name: APP_URL from: runtime.urls.app default: http://localhost - name: TOOL_ADMINER from: services.adminer.enabled format: bool when: services.adminer.enabled - name: TOOL_REDIS from: services.redis_insight.enabled format: bool when: services.redis_insight.enabledworkspace.yml:
project: name: demodwe render env на macOS произведёт:
# Generated by dwe — do not edit manually
PROJECT=demoUID=1000GID=1000COMPOSE_PROJECT_NAME=demoAPP_PORT=8080APP_URL=http://localhostTOOL_ADMINER=trueРазбор:
COMPOSE_PROJECT_NAME— нет ниworkspace/docker.yml, ниproject.prefix, поэтому имя compose-проекта откатывается кproject.nameв нижнем регистре. Приproject: {prefix: dwe, name: Demo}эти две строки читались бы какPROJECT=DemoиCOMPOSE_PROJECT_NAME=dwe-demo.APP_PORT—format: int, значение8080, выводится напрямую.APP_URL—fromразрешается в пустую строку (ложное приformat: string), поэтому используетсяdefault.TOOL_ADMINER—whenистинно, значениеtrueвыводится как литералtrue.TOOL_REDIS—whenразрешается в отсутствующее (нет записиredis_insight), правило пропускается, строка не выводится.
Частые ловушки
Заголовок раздела «Частые ловушки»format: stringглотаетfalse/0/""— если в выводе нужен литералfalseили0, выбирайтеformat: boolилиformat: int. Иначе значение молча провалится вdefault(или в пустую строку).whenиfrom— независимые dot-path —whenнеобязательно указывает на тот же ключ, что иfrom. Используйте его, чтобы гейтить одну переменную по другой настройке (например,from: services.second.container,when: services.second.enabled).required: trueбезdefault— даёт жёсткую ошибку, если путь отсутствует. Применяйте для переменных, без которых не стартует ваш runtime; иначе полагайтесь наdefault, чтобы файл был полным.- Правка
.envвручную — файл перегенерируется черезdwe render env --out .envи lifecycle-хуки. Правьтеexportsвworkspace/defaults.ymlили оверрайды вworkspace/local.yml. - У
--outнет короткой формы. Флаг называется--out; алиаса-oнет. Используйтеdwe render env --out .env. - Многострочное значение отвергается. Значения пишутся без кавычек, поэтому compose прочитал бы вторую и последующие строки как отдельные записи
.env: значение приедет обрезанным, а строка видаNAME=…объявит переменную, которую никто не объявлял. Вместо этогоdwe render envпадает и называет путь-источник. Доставляйте PEM-ключ или blob сервисного аккаунта через файл config-пака (render config), который умеет.age-источники нативно. См. Только однострочные значения. - Переобъявление
PROJECT/UID/GID/COMPOSE_PROJECT_NAME— эти имена зарезервированы и валидируются на загрузке конфигурации. Правило экспорта, использующее одно из них, даёт жёсткую ошибку при загрузке проектной конфигурации любой командой; уберите правило и берите то же значение из системной переменной. Имя compose-проекта задаётся черезproject_nameвworkspace/docker.yml— именно за ним следует выводимая строка.
Связанные справочники
Заголовок раздела «Связанные справочники»- схема правил
exports.env— полный справочник по полям и форматам - Разрешение dot-path — как пути
fromиwhenходят по объединённой конфигурации - secrets — зашифрованные значения
vars.*, ключи и почему нерасшифрованное значение здесь жёсткая ошибка - Запустите
dwe render env --help, чтобы увидеть актуальный CLI-интерфейс