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

Шаблоны

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

МестоСинтаксисКонтекстЗаметки
info.ymltext, 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.ymlwhen: type: template, expr:{{ ... }}Разрешённая конфигурация проектаВычисляется на этапе планирования. См. deploy
deploy.yml / lifecycle.yml / reset.ymlcmd, строковые листья with, check, timeout и shell when: cmd:Только ${...} (известные head’ы)Смерженная конфигурация проекта (.Raw)Рендерится один раз на этапе разрешения плана, не в момент выполнения — до того, как шаг будет показан, хеширован или запущен. См. Шаблоны в полях шага
Билтин messagetext:{{ ... }}Разрешённая конфигурация проектаСм. билтин message
docker.ymlproject_nameТолько ${...}Разрешённая конфигурация проекта (lookup’ы по .Raw)Только dot-path lookups (без {{ }}-логики). См. docker.md
workspace/templates/git/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg)Строгий режим. См. render/git.md
workspace/templates/ide/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg)Строгий режим. См. render/ide.md
workspace/templates/ai/<pack>/**/*.tmpl{{ ... }}Контекст render-пака (.Project, .Service, .Resolved, .ServiceCfg, .Runtime, .Services, .Cfg)Строгий режим. См. render/ai.md
workspace/templates/config/<pack>/**${...}Разрешённая конфигурация проекта (.Raw) + курируемый поднабор ${services.<name>...} + ${generated.<name>}Lenient (отсутствующее → ""). См. render/config.md
params.*.default_from, context.*.fromТолько plain dot-paths (без template-выражений).

Существуют два слоя интерполяции; оба вычисляются одним движком.

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

{{ ... }} — полный Go text/template. Условия, циклы, пайплайны, helper-функции. Доступно везде, где вычисляются шаблоны.

# Микс в одной строке (место команды)
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, state, 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, строгие):

ПеременнаяИсточник
.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" }} для ключей с дефисами, точками, ведущими цифрами и т.д. Для типовых случаев предпочитайте выделенные поля выше.

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

Стандартная библиотека предоставляет следующие функции из коробки. Полный справочник: 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 -}}"

Единственный хелпер, специфичный для проекта. Строит 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 доступны везде, где вычисляются шаблоны.

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

Герметичность по построению. Набор хелперов собран без единой функции, которая обращалась бы к окружению, файловой системе, сети или random/crypto-источникам. Sprout-функции shuffle (math/rand, засеянный из crypto) и 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 парсят шаблоны с семантикой {{.Option "missingkey=error"}}: опечатка вроде {{.Servic.Name}} прерывает рендер всего пака целиком, а не пишет <no value> на диск. Защищайте действительно опциональные поля через {{if ...}}:

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

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

ЗадачаСниппет
Текущая дата{{ 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-вызовы до парсинга.