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

Шаблоны

Go-шаблоны вычисляются в элементах info-дашборда, декларативных командах, условиях when: пайплайнов, билтине message и render-паках IDE / AI / git / workspace / config. Эта страница описывает синтаксис, контекст и доступные функции. Info, команды, условия и message используют библиотеку go-sprout. Паки ide / ai / git / workspace используют строгий Go text/template только со встроенными функциями; Sprout и appURL в них недоступны. В workspace-паках только источники .tmpl вычисляются как шаблоны, остальные копируются побайтно. Config-паки используют подложку ${...} (lenient — отсутствующее → ""); см. render config.

МестоСинтаксисКонтекстЗаметки
info.yml — text, value, when{{ ... }}Разрешённая конфигурация проектаСм. info.md
workspace/commands/ — cmd, argv, workdir, compose_args, env, messages.*, confirmation_text, files.*.path/candidates, workflow-шаги steps[].with[<key>] / steps[].when${...} и {{ ... }}Контекст команды (.Raw + .Params + .Context + .Files + .Host)См. commands/
deploy.yml / lifecycle.yml / reset.yml — when: type: template, expr:{{ ... }}Разрешённая конфигурация проектаВычисляется на этапе планирования. См. deploy
deploy.yml / lifecycle.yml / reset.yml — cmd, строковые листья with, check, timeout и shell when: cmd:Только ${...} (известные head’ы)Смерженная конфигурация проекта (.Raw)Рендерится один раз на этапе разрешения плана, не в момент выполнения — до того, как шаг будет показан, хеширован или запущен. См. Шаблоны в полях шага
Билтин message — text:{{ ... }}Разрешённая конфигурация проектаСм. билтин message
docker.yml — project_nameТолько ${...}Разрешённая конфигурация проекта (lookup’ы по .Raw)Только dot-path lookups (без {{ }}-логики). См. docker.md
workspace/templates/git/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg, .Commands, .CommandGroups)Строгий режим. См. render/git.md
workspace/templates/ide/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg, .Commands, .CommandGroups)Строгий режим. См. render/ide.md
workspace/templates/ai/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg, .Commands, .CommandGroups)Строгий режим. См. render/ai.md
workspace/templates/config/<pack>/**${...}Разрешённая конфигурация проекта (.Raw) + курируемый поднабор ${services.<name>...} + ${generated.<name>}Lenient (отсутствующее → ""). См. render/config.md
workspace/templates/workspace/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Runtime, .Services, .Cfg, .Commands, .CommandGroups); .Service / .Resolved пусты, .ServiceCfg — zero-valueСтрогий режим; остальные источники копируются побайтно. См. render/workspace.md
params.*.default_from, context.*.from——Только plain dot-paths (без template-выражений).

Существуют два слоя интерполяции; их доступность зависит от места использования в таблице выше.

${...} — shorthand lookups. Компактный, без логики. Используется в определениях команд и в project_name из docker.yml. Компилятор переписывает каждое ${...} в эквивалентное выражение {{ ... }} на этапе разбора.

{{ ... }} — полный Go text/template. Условия, циклы, пайплайны, helper-функции. Доступность дополнительных функций зависит от места использования; config-паки документируют подложку ${...}.

# Микс в одной строке (место команды)
path: "${param.dump_dir}/${param.database}{{ if .Params.dump_date }}_{{ now | date \"2006-01-02\" }}{{ end }}.sql.gz"

Практическое правило: используйте ${...} для простых lookup’ов; переходите к {{ ... }} всякий раз, когда нужно условие, сравнение, default, преобразование строки или пайплайн.

${...} разрешается через неймспейсы; первый сегмент маршрутизируется к конкретному источнику данных:

ВыражениеРезолвится как
${vars.db.user}Dot-path в смерженном dwe-конфиге (Raw)
${param.<name>}Разрешённое значение параметра
${context.<name>}Разрешённое значение контекста
${files.<id>.path}Абсолютный путь разрешённого файла-артефакта
${host.uid} / ${host.gid}Эффективные UID/GID (1000:1000 на macOS, реальные значения на Linux)
${generated.<name>}Значение по сервису, собранное в .dwe/generated.yml (только config-render-паки; отсутствующее → ""). См. render/config.md

Всё, чей head — это ключ корня смерженного конфига (project, services, vars, exports, compose, update, bridge, schema_version, …), трактуется как dot-path lookup в Raw. Это whitelist, а не «всё, что иначе не сматчилось»: нераспознанный head — shell-подобный ${HOME}/${PATH}, случайный знак доллара, опечатка или устаревший bare dot-path времён до строгого корня вроде ${databases.main} — остаётся буквальным ${...} вместо того, чтобы молча схлопнуться в "". Это особенно важно в pipeline cmd:, который теперь рендерится (см. таблицу выше): shell-переменная вроде ${CONTAINER} в команде docker inspect доходит до sh без изменений, а не проглатывается. Для пользовательских значений конфига предпочитайте ${vars.*} — они живут под блоком vars: в YAML; строгий корень отвергает свободные ключи верхнего уровня, поэтому vars: — их единственный дом. Известный head, чей оставшийся путь не резолвится (например, опечатка под vars:), рендерится в "" — dwe validate ловит этот случай для шагов пайплайна (см. config.template_refs). Литерал $$ пропускается без изменений.

Ссылкой считается только запись с dot-path. Все формы неймспейсов выше — с точкой (${vars.db.host}, ${project.name}, ${host.uid}, ${files.<id>.path}), поэтому запись из одного лишь head’а — ${host} / ${files} / ${services} — трактуется как shell-переменная, случайно совпавшая с именем неймспейса, и остаётся буквальной ровно так же, как ${HOME}. ${args} — единственная bare-форма, определённая синтаксисом, и она сохраняет своё значение (см. commands/directives.md).

Одно задокументированное исключение: поле project_name в docker.yml использует отдельный, более строгий резолвер (resolveVarTemplate), появившийся ещё до этого whitelist’а — у него нет ограничения по неймспейсам (резолвится любой dot-path в Raw), но неразрешённый путь — это ошибка, а не буквальный текст, поскольку сломанное имя compose-проекта должно упасть громко, а не молча передать неразрешённую строку ${...} в docker compose -p.

Строка между {{ }} одинаковая в любой форме YAML — меняется только обёртка:

# скаляр в двойных кавычках: внутренний " нужно экранировать как \"
path: ".dwe/logs/{{ now | date \"2006-01-02\" }}.log"
# скаляр в одинарных кавычках: экранирование не требуется (рекомендуется для шаблонов)
path: '.dwe/logs/{{ now | date "2006-01-02" }}.log'
# литеральный блок-скаляр: экранирование не требуется
cmd: |
echo "{{ now | date "2006-01-02" }}"

Предпочитайте скаляры в одинарных кавычках ('...') для однострочных шаблонов, тело которых содержит ". Двойные кавычки ("...") оставляйте для строк, которым нужны YAML escape-последовательности \n/\t. \|, который встречается в таблицах на этой странице, — это экранирование markdown-ячеек для отрендеренных доков; в YAML всегда используется обычный | внутри {{ }}.

Данные, доступные шаблону, зависят от места. Доступ к полям — через точечный синтаксис (.Project.Name).

Команды:

ПутьСодержимое
.RawСмерженные workspace.yml + defaults.yml + local.yml как вложенная карта
.ParamsРазрешённые значения параметров (map по имени параметра)
.ContextРазрешённые значения контекста (map по имени контекста)
.FilesРазрешённые файлы-артефакты (map по file id; у каждого есть поле .Path)
.Host.UID / .Host.GIDСтроки UID/GID хоста

Info, пайплайны, билтин message: разрешённая конфигурация проекта — адресуется тем же точечным синтаксисом, что и .Cfg render-пака ниже (например, .Project.Name, ((index .Services "main").Port "http"), (index .Services "catalog").Enabled).

Render-паки (git / ide / ai / workspace, строгие):

ПеременнаяИсточник
.Projectблок project: из workspace.yml
.Serviceканоническая идентичность конфига — корень цепочки extends: рендерящегося сервиса (равно .Resolved, когда цепочки extends нет)
.Resolvedидентичность рендера — ключ карты сервиса, который фактически рендерится (победитель политики коллизий)
.ServiceCfgэффективная конфигурация сервиса после разрешения extends
.Runtimeсмерженный блок runtime (.Runtime.UseHTTPS, .Runtime.SPX.Path). Порты / хосты на сервис находятся в каждой записи сервиса (см. .Services ниже).
.Servicesсервисы по имени. Используйте (index .Services "<name>") для выборки; хелперы записи .Port "<port-name>" / .Host "<host-name>" / .PortScheme "<port-name>" (возвращает "", если переопределения нет) / .EffectiveScheme "<port-name>" .Runtime.UseHTTPS (возвращает "http" / "https" после прохода по цепочке per-port → сервис → runtime). Подмножества по типу — через .AppServices / .ToolServices / .InfraServices.
.Cfgобъединённая конфигурация проекта (продвинутое). .Cfg.Raw — это дерево конфига после слияния (services.* подставляется из per-service файлов service.yml). Точечный синтаксис (.Cfg.Raw.git.project_prefix) работает только для identifier-safe ключей; используйте {{ index .Cfg.Raw "my-key" }} для ключей с дефисами, точками, ведущими цифрами и т.д. Для типовых случаев предпочитайте выделенные поля выше.
.Commands / .CommandGroupsобъявленные команды проекта и авторские группы команд, плюс методы .ServiceCommands / .ServiceCommandGroups — см. Индекс объявленных команд

Workspace-паки используют тот же контекст, но без выбора сервиса: .Service и .Resolved пусты, .ServiceCfg — zero-value. Методы .ServiceCommands и .ServiceCommandGroups возвращают пустые списки; .Commands и .CommandGroups содержат индекс всего проекта.

IDE-, AI- и workspace-паки рендерятся в отслеживаемые файлы проекта. Избегайте использования developer-local или секретных ключей через .Cfg.Raw в этих шаблонах — значения из local.yml дадут разные диффы у разных разработчиков. Git-хуки рендерятся в .git/hooks/ (gitignored) и под это ограничение не попадают.

Паки ide / ai / git / workspace никогда не видят расшифрованный секрет

Заголовок раздела «Паки ide / ai / git / workspace никогда не видят расшифрованный секрет»

Поскольку их выводы обычно отслеживаются git, эти четыре рендерера грузят санитизированный конфиг: та же трёхслойная сборка, но вообще без прохода расшифровки. Каждое поле, доступное шаблону — .Cfg.Raw, .Cfg, .Project, .Runtime, .Services, .ServiceCfg, — несёт закоммиченный маркер ENC[age:…] там, где настоящий рантайм-конфиг несёт открытый текст.

Поэтому шаблон, читающий зашифрованное значение, выдаёт шифротекст (уже закоммиченный, безвредный), но не открытый текст — без учёта путей и без неоднозначности для последовательностей и точечных ключей. Это структурная гарантия, а не allowlist, — именно поэтому совет «избегайте секретных ключей» выше про шум в диффах, а не про утечку.

Конфиг-паки (render config) — противоположный случай: они рендерятся в hub-директорию сервиса из gitignore по настоящему конфигу и вдобавок поддерживают целые источники .age — см. render config.

Стандартная библиотека предоставляет следующие функции из коробки. Полный справочник: pkg.go.dev/text/template#hdr-Functions.

ФункцияПрименение
eq, ne, lt, le, gt, geСравнение
and, or, notБулева логика
lenДлина строки / слайса / map
indexИндексация map / слайса
printfФорматированные строки (Go format verbs)
print, printlnКонкатенация
html, js, urlqueryЭкранирование

Управляющие конструкции: {{ if }}, {{ range }}, {{ with }}, {{ define }} / {{ template }}.

# вывести флаг только если bool-параметр true
argv:
- "{{ if .Params.fresh }}--fresh{{ end }}"
# вложенный if / else if / else
env:
LOG_LEVEL: |-
{{ if eq .Params.profile "prod" }}error
{{ else if eq .Params.profile "stage" }}warn
{{ else }}debug{{ end }}
# range с индексом
env:
TAGS: "{{ range $i, $t := .Params.tags }}{{ if $i }},{{ end }}{{ $t }}{{ end }}"
# with / default
cmd: "mariadb -u${vars.db.user}{{ with .Params.database }} -D{{ . }}{{ end }}"
env:
REGION: '{{ or .Params.region "us-east-1" }}'

{{- ... -}} срезает окружающие пробельные символы. Полезно, когда многострочный {{ if }}-блок должен рендериться в один shell-аргумент:

cmd: |-
echo "{{- if .Params.verbose -}}verbose{{- else -}}quiet{{- end -}}"

Хелпер доступен в info, командах, условиях пайплайнов и message, но не в паках ide / ai / git / workspace. Единственный хелпер, специфичный для проекта. Строит URL из хоста, порта, HTTPS-флага и опционального path. Порт опускается, если совпадает с дефолтом схемы (80 для http, 443 для https).

Сигнатура: appURL host port useHTTPS [path]

# Хостнейм приложения + порт приложения (проксируется через main-сервис)
value: '{{ appURL ((index .Services "main").Host "web") ((index .Services "main").Port "http") .Runtime.UseHTTPS }}'
# → "http://laravel.localhost" или "https://laravel.localhost"
# Хостнейм инструмента + порт реверс-прокси main (инструмент маршрутизируется через main-приложение, а не через свой прямой порт)
value: '{{ appURL ((index .Services "adminer").Host "web") ((index .Services "main").Port "http") .Runtime.UseHTTPS "/login" }}'
# → "http://adminer.localhost/login"

Следующие регистры из go-sprout доступны в info, командах, условиях пайплайнов и message. Паки ide / ai / git / workspace используют только встроенные функции text/template; эти регистры в них недоступны.

РегистрПримерыОписание
stddefault, ternary, empty, coalesceДефолты, условия, проверки на пустоту
stringshasSuffix, hasPrefix, toLower, toUpper, trim, replace, splitМанипуляции со строками
numericadd, sub, mul, div, max, minЧисловые операции
slicesfirst, last, slice, join, reverse, uniq, hasОперации над списками/массивами
mapskeys, values, hasKey, pick, omitОперации над map’ами/объектами
regexregexMatch, regexReplaceAll, regexSplitСопоставление по регулярным выражениям
conversiontoInt, toFloat64, toString, toBoolПреобразование типов
timenow, date, dateInZone, durationОперации с датой/временем
filesystempathBase, pathDir, pathExt, pathClean, osBase, osDirМанипуляции с путями
semversemver, semverCompareОперации над семантическими версиями

Значение — последним аргументом. Почти все функции sprout принимают map, список или строку, над которыми работают, последним аргументом (merge, mergeOverwrite и concat берут целевой map первым), поэтому естественно ложатся в пайп: {{ $m | pick "a" }}, {{ $list | append "x" }}, {{ $name | regexReplaceAll "-" "_" }}. Шаблоны, написанные под порядок аргументов Sprig, нужно переписать — см. Обновление DWE.

Герметичность по построению. Набор хелперов собран без единой функции, которая обращалась бы к окружению, файловой системе, сети или random/crypto-источникам. Sprout-функции shuffle (случайный результат) и hello (debug-заглушка) намеренно удалены.

Полную документацию по каждой функции см. в справочнике регистров sprout.

Четыре дополнительных хелпера доступны только внутри шаблонов workspace/commands/. Они принимают сырые map’ы и проходят по dot-path’ам, возвращая "" для отсутствующего ключа (без template-ошибки).

ХелперСигнатураПрименение
resolveresolve .Raw "vars.db.host"Dot-path lookup в смерженном конфиге. Эквивалентно ${vars.db.host}.
resolveMapresolveMap .Params "name"Lookup ключа в плоской map[string]any. Эквивалентно ${param.name} / ${context.name}.
resolveFileresolveFile .Files "id" "path"Lookup подключа в разрешённом файле-артефакте. Эквивалентно ${files.id.path}.
resolveGeneratedresolveGenerated .Generated "app_key"Per-service значение, собранное (harvested) на проходе config-рендера. Эквивалентно ${generated.app_key}.

Они существуют, чтобы shorthand ${...} мог разворачиваться в переносимую Go-template форму и чтобы авторы могли дотянуться до сырого конфига, когда точечный стиль .Raw.<x>.<y> неудобен (ключи с точками, числовые ключи и т.д.).

render ide, render ai, render git и .tmpl-источники render workspace парсят шаблоны с опцией missingkey=error: опечатка вроде {{.Servic.Name}} прерывает рендер всего пака целиком, а не пишет <no value> на диск. Защищайте действительно опциональные поля через {{if ...}}:

{{if .ServiceCfg.CLI.Workdir}}WORKDIR={{.ServiceCfg.CLI.Workdir}}{{end}}

В render workspace все выбранные паки проверяются и рендерятся в памяти перед любой записью. Остальные источники копируются побайтно, поэтому плейсхолдеры других инструментов вроде {{PLAN_FILE}} сохраняются.

Другие места (info, commands, условия пайплайнов, message) используют мягкий рендер — отсутствующий ключ резолвится в <no value> или пустую строку, никогда не в ошибку.

Паттерны с Sprout-хелперами или appURL ниже относятся к info / commands / условиям пайплайнов / message; в паках ide / ai / git / workspace доступны только встроенные функции Go-шаблонов.

ЗадачаСниппет
Текущая дата{{ now | date "2006-01-02" }}
Текущие дата и время{{ now | date "2006-01-02_15-04-05" }}
Базовое имя пути{{ .Params.script_path | pathBase }}
Директория пути{{ .Params.script_path | pathDir }}
Default / fallback{{ .Value | default "N/A" }} или {{ or .Params.region "us-east-1" }}
Условное значение{{ if eq .State "ready" }}Ready{{ else }}Not ready{{ end }}
Блок с защитой от пустоты{{ with .Params.database }} -D{{ . }}{{ end }}
Объединить список{{ join "," .Params.tags }}
Lookup сырого конфига{{ resolve .Raw "vars.db.host" }} (только команды)
Сборка URL{{ appURL ((index .Services "main").Host "web") ((index .Services "main").Port "http") .Runtime.UseHTTPS }}
  • Предпочитайте path* вместо os* для путей в контейнерах. pathBase / pathDir используют семантику прямого слэша; osBase / osDir следуют разделителю хостовой ОС. Пути в контейнерах должны быть предсказуемыми при рендере на macOS-хостах — придерживайтесь вариантов path*, если только вам не нужно специфичное для ОС поведение.

  • date — это фильтр, не конструктор. Он принимает строку-формат и time.Time, а не наоборот:

    • {{ now | date "2006-01-02" }} ✓
    • {{ date "2006-01-02" }} ✗ (нет значения времени)

    Строка-формат использует референсное время Go Mon Jan 2 15:04:05 MST 2006 — см. шпаргалку по форматированию даты/времени в Go.

  • Truthiness when:. Отрендеренное значение when: считается truthy, если только оно не равно "", "false" или "0" (после trim). Сравнения, возвращающие Go-bool, рендерятся как "true"/"false"; сравнения, возвращающие integer-like значение (например, длины), рендерятся как десятичные строки.

  • Никаких env, FS, сети или случайности. Шаблоны вычисляются в герметичном FuncMap по построению. Если шаблону нужно состояние проекта, выставьте его через разрешённую конфигурацию проекта (info / пайплайны) или через декларацию context.<name>: from: <dot.path> (команды).

  • В командах смешивать ${...} и {{ ... }} нормально. Они используют один контекст и рендерятся за один проход — ${...} переписывается в template-вызовы до парсинга.