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

deploy.yml / reset.yml

Декларации пайплайнов деплоя и сброса.

workspace/deploy.yml объявляет пайплайн деплоя-оркестратора. workspace/reset.yml объявляет деструктивный пайплайн сброса. Пайплайны деплоя отдельных сервисов лежат в workspace/services/<service>/deploy.yml.

Все три файла загружаются отдельно и не участвуют в трёхслойном мердже.

И workspace/deploy.yml, и workspace/reset.yml опциональны. При их отсутствии DWE подставляет встроенный пайплайн по умолчанию и выводит одну info-строку в stderr: Using built-in default <deploy|reset> pipeline (override with workspace/<deploy|reset>.yml). Эта строка подавляется в режиме --output json.

Дефолтный пайплайн деплоя (срабатывает, когда workspace/deploy.yml отсутствует):

Фазы: services (запускает deploy_services: true для встраивания пайплайнов включённых сервисов) → start (type: dwe, cmd: "docker up --wait") → post-deploy (отображение info + сообщение об успехе).

Дефолтный пайплайн сброса (срабатывает, когда workspace/reset.yml отсутствует):

Фазы: pre (промпт подтверждения) → stop (type: dwe, cmd: "docker down") → cleanup (удаление томов, удаление директории services/).

ФайлРоль
workspace/deploy.ymlОркестратор верхнего уровня: перечисляет фазы по порядку, ссылается на пайплайны сервисов
workspace/services/<svc>/deploy.ymlФазы и шаги отдельного сервиса (встраиваются оркестратором при deploy_services: true). Пайплайн деплоя может быть у сервиса любого типа (app, tool, infra).
workspace/reset.ymlОтдельный пайплайн сброса, исполняемый через dwe reset run. Фазы deploy_services отвергаются.
flowchart TB
  D[workspace/deploy.yml] -->|phase: deploy_services| INL{Встраивание включённых сервисов}

  subgraph svc["workspace/services/&lt;service&gt;/deploy.yml — по одному файлу на сервис"]
    direction TB
    S1["обязательный сервис<br/>(всегда встраивается)"]
    S2["опциональный сервис A<br/>(встраивается, если включён)"]
    S3["опциональный сервис B<br/>(встраивается, если включён)"]
    SN["…N сервисов"]
  end

  svc --> INL
  INL -->|топосортировка по after:| PLAN[Разрешённый план]
  PLAN --> RUN[(PlainReporter — ✓ ✗ ◎ ·<br/>.dwe/logs/deploy.log)]

  R[workspace/reset.yml] --> RPLAN[Разрешённый план] --> RUN2[(PlainReporter)]

Сервис любого типа (app, tool или infra) может иметь workspace/services/<name>/deploy.yml. На этапе планирования оркестратор отфильтровывает этот набор до включённых сервисов (обязательные всегда включены) и встраивает их в топологическом порядке after:. Сервисы без файла деплоя молча пропускаются — не каждому сервису он нужен.

Поле after: в workspace/services/<name>/deploy.yml задаёт порядок деплоя между сервисами (отдельно от рантайм-зависимости depends_on:). Подробности см. в Поля верхнего уровня.

log: true # optional: tee output to .dwe/logs/<pipeline>.log
phases:
# Normal phase: supports when, untracked, and steps
- name: <phase-name>
description: Human-readable description
when: # optional: pre-condition (typed condition)
type: builtin|shell|template
cmd: <string> # for builtin/shell
expr: <string> # for template
untracked: true # optional: suppress step output for this phase
steps:
- name: <step-name>
description: Human-readable description
type: shell|dwe|command|builtin # execution type (required)
cmd: <value> # command payload (required)
when: # optional: pre-condition (typed condition)
type: builtin|shell|template
cmd: <string> # for builtin/shell
expr: <string> # for template
check: # optional: post-condition (typed action, либо скаляр `auto`)
type: shell|dwe|command|builtin
cmd: <value>
with: # optional: parameters
key: value
continue_on_error: true # optional: failure does not abort the pipeline
skip_confirm: true # optional: bypass confirmation prompts for this step
untracked: true # optional: exclude this step from [N/M] counter and suppress its output
files_gate: readable # short form: state must be readable|missing
# or long form:
files_gate:
state: readable|missing # required
command: <cmd-id> # default: step.cmd (only valid for type: command)
require: required|all|[id1, id2] # default: required
with: # default: step.with
key: value
with: # parameters (for command and builtin types)
key: value
# deploy_services phase (deploy.yml only): no steps or when allowed
- name: services
description: Human-readable description
deploy_services: true # orchestrator marker; mutually exclusive with steps and when
ПолеТипПо умолчаниюОписание
logbooldeploy.yml: true; reset.yml: falseДублировать статусные сообщения DWE и stdout/stderr дочерних процессов в .dwe/logs/<pipeline>.log (ANSI-коды вырезаются).
phaseslistУпорядоченный список фаз.
afterlist of strings[]Только в deploy.yml отдельного сервиса. Декларирует порядок деплоя: данный сервис деплоится после перечисленных. Опущенное или пустое значение означает отсутствие ограничения порядка. Отличается от рантайм-зависимости depends_on: (которая управляет порядком запуска контейнеров) — используйте after:, когда хотите, чтобы шаги деплоя одного сервиса завершились до начала шагов другого. Недопустимо в workspace/deploy.yml, workspace/reset.yml и workspace/services/<name>/reset.yml (ошибка на этапе загрузки). Полный деплой (dwe deploy run) топосортирует сервисы по after:; dwe deploy run --service <name> НЕ каскадирует на объявленные after: зависимости (явный выбор перекрывает порядок).
ПолеТипПо умолчаниюОписание
namestringобязательноУникальный ключ фазы внутри пайплайна
descriptionstringопциональноПоказывается в выводе deploy plan
whentyped conditionПредусловие; фаза пропускается при ложном значении (см. Условия). Не допускается на фазах deploy_services.
untrackedboolfalseЕсли true, шаги фазы исключаются из счётчика шагов и не дают системного вывода
deploy_servicesboolfalseМаркер оркестратора: CLI встраивает сюда пайплайны сервисов в порядке зависимостей. Фаза deploy_services не должна содержать steps или условие when — оба случая являются жёсткими ошибками на этапе загрузки.
ПолеТипОписание
namestringУникальный ключ шага внутри фазы
descriptionstringПоказывается в выводе deploy plan
typeenumТип исполнения: один из shell, dwe, command, builtin (обязательно). См. Типы исполнения шагов.
cmdstringПолезная нагрузка команды (обязательно); содержимое зависит от type
withmappingПараметры, передаваемые в команду или билтин (опционально; обязательно для большинства билтинов)
whentyped conditionПредусловие, вычисляемое до запуска шага; шаг пропускается при ложном значении. См. Условия.
checktyped action, либо скаляр autoПостусловие, вычисляемое после успеха шага; пайплайн прерывается, если действие провалилось. Пропускается, когда continue_on_error: true и шаг провалился. См. Условия.
files_gatetyped gateПредусловие на основе наличия/отсутствия файлов из блока files: команды. Шаг пропускается, если не удовлетворено. См. files_gate:.
continue_on_errorboolЕсли true, упавший шаг фиксируется через FailStep (красный ✗), но пайплайн не прерывается. После такого шага пропускаются check и хук следующего шага. Полезно для опциональных хук-фаз — см. lifecycle.yml. Если тело шага успешно, но check: падает и continue_on_error: true, шаг отмечается как упавший, а пайплайн переходит к следующему шагу (симметрично семантике падения тела).
skip_confirmboolЕсли true, обходит промпты подтверждения только для этого шага — эквивалент шагового -y / --yes. Распространяется на тело шага и его действие check:. Объединяется по ИЛИ с пайплайновым флагом skip-confirm, поэтому шаг становится неинтерактивным, если задан хотя бы один из них. Полезно, когда основная часть пайплайна интерактивна, но один шаг (например, билтин confirm, защищающий идемпотентное действие, или команда с внутренним перепромптом) должен всегда продолжать.
untrackedboolЕсли true, шаг исключается из счётчика [N/M], а его lifecycle-вывод (строки start/done) подавляется. Падения всё равно показываются. Объединяется по ИЛИ с фазовым untracked — используйте шаговый флаг, чтобы скрыть единственный шаг stack-up или wait-healthy без вынесения в отдельную untracked-фазу. Допустим на шагах параллельной группы; под-шаги наследуют untracked-статус от группы.
timeoutduration stringОпциональный бюджет по времени на тело этого шага, например 30s, 2m. Опционально — никакого неявного значения по умолчанию нигде: отсутствие поля или timeout: 0 означает отсутствие ограничения, ровно как если бы поля не было вовсе. Положительное значение ограничивает только этот один шаг (или, внутри группы parallel:, только этот под-шаг) через context.WithTimeout вокруг тела шага; действие check: этим не ограничивается. Таймаут работает через отмену контекста, поэтому он ограничивает только тело, которое учитывает ctx, — подпроцессы type: shell/type: dwe (сначала SIGTERM, затем принудительное завершение после паузы), учитывающие ctx билтины (http_check, docker_wait_healthy, tcp_reachable, …), и шаги type: command, чья собственная работа учитывает ctx. Тело, заблокированное на интерактивном вводе (например, билтин confirm, ожидающий stdin), не прерывается принудительно — дедлайн наступает, но горутина остаётся заблокированной до получения ввода; таймаут на человеческом промпте всё равно бессмысленен, так что это осознанное ограничение, а не баг. Отрицательная длительность ("-1s") отклоняется на этапе разрешения пайплайна. Общее поле движка: работает одинаково в deploy.yml, reset.yml, lifecycle.yml и в шагах сценариев workspace/tests/*.yml.

Шаг также может объявить блок parallel: вместо листовых полей тела (type / cmd). См. Параллельные группы шагов.

cmd, строковые листья with, check, timeout и shell-условие when: рендерятся на этапе разрешения плана — до того, как шаг будет показан или выполнен. Ссылка ${...} с известным пространством имён (vars, services, project, …) подставляется своим фактическим значением; dwe deploy plan поэтому печатает то, что реально будет выполнено, а не буквальный текст ${vars.*} из deploy.yml. Ссылка ${...} с неизвестным пространством имён (shell-подобная ${HOME}, опечатка) — либо запись из одного head’а без dot-path, например shell-переменная ${host} / ${files} — остаётся буквальным текстом и, если она попадает в вывод плана, помечается аннотацией [unresolved: ${...}] в конце строки (также передаётся как поле unresolved в --output json) — эта аннотация — лишь подсказка для отображения и никогда не добавляется в --format shell, который должен оставаться напрямую исполняемым.

Поле шага, содержащее ${...} с известным head’ом, не должно одновременно содержать литеральный {{ }}. Подстановка прогоняет поле через тот же движок Go-шаблонов, в который компилируется shorthand ${...}, поэтому формат-строка docker в той же команде — cmd: 'docker inspect -f "{{.State.Status}}" ${vars.container}' — интерпретируется как шаблон и валит разрешение с ошибкой can't evaluate field State. Весь dwe deploy plan / dwe deploy run на этом останавливается. Либо разведите их (вынесите формат-строку в скрипт или значение ${vars.*} в shell-переменную), либо экранируйте скобки как {{"{{"}}. Поле cmd, check, timeout или when: без ссылок с известным head’ом в движок вообще не попадает, поэтому обычный docker inspect -f "{{.State.Status}}" app — или команда, использующая только shell-переменную вроде ${CONTAINER} — передаётся без изменений.

Единственное исключение — строковые листья with:, и только там, где значение попадает в пользовательскую команду: шаг или action с type: command, собственный with: у files_gate:, а также with: самого шага, когда files_gate: не объявил своего (тогда гейт наследует карту шага как свои параметры). Там лист рендерится, если содержит ${...} с известным head’ом или литеральный {{, потому что это значение with: и раньше всегда проходило через этот шаблонный движок перед попаданием в команду. Поэтому with: {host: '{{ resolve .Raw "vars.db.host" }}'} по-прежнему резолвится, а ограничение на смешанную форму выше распространяется и на такие листья.

У with: шага с type: builtin остаётся узкий гейт: ${...} с известным head’ом подставляется, литеральный {{ }} проходит нетронутым. Это верно и когда карту наследует files_gate:: расширение гейта выше действует для любых других типов шагов, но никогда — для параметров builtin’а. Builtin’ы получают значения with: как есть, и некоторые рендерят их в другом шаблонном пространстве: message рендерит свой text как Go-шаблон над DweConfig ({{ .Project.Name }}), а предикат shell принимает строки формата docker inspect -f "{{.State.Status}}". Оба продолжают работать без изменений.

В шаге пайплайна резолвятся только пути конфига проекта и ${host.uid} / ${host.gid}. Пространства имён, принадлежащие пользовательской команде или проходу рендера конфигов — ${param.*}, ${context.*}, ${files.*}, ${generated.*} и ${args} — здесь источника не имеют, поэтому ссылка на любое из них — ошибка разрешения с указанием пространства имён, а не молчаливая подстановка пустой строки:

step "checkout": cmd: template uses ${param.branch}: the "param" namespace has no source here
(only project config paths and ${host.*} resolve on this path)

${snapshot.*} вне snapshot-воркфлоу падает точно так же. Чтобы передать значение в шаг, положите его в vars: и ссылайтесь через ${vars.*}; чтобы задать параметры шагу с type: command, используйте его собственный with: — эта карта задаёт ${param.*} целевой команды, а не читает их. То же правило действует для шагов сценариев workspace/tests/*.yml, которые рендерятся через тот же субстрат.

Поскольку фактический текст отрендеренного шага теперь зависит от блока vars:, весь блок vars: включается и в проектный, и в посервисный хеш конфига, поэтому изменение используемого значения перезапускает использующие его шаги, вместо того чтобы деплой отрапортовал устаревшее «already up-to-date». Разовое последствие при обновлении: это меняет хеш конфига проекта для каждого существующего проекта, поэтому первый dwe deploy run после обновления на версию dwe с этим изменением один раз перезапустит все шаги, даже без каких-либо ваших правок в vars:. Шаги по умолчанию должны быть идемпотентными и защищёнными гейтами, так что это безопасно, просто заметно — не пугайтесь, если деплой, неделями остававшийся «up-to-date», вдруг один раз перезапустит всё.

Фаза post-deploy (по соглашению — последняя фаза в deploy.yml) выполняется только если все предыдущие фазы успешны. В этом нет магии — это следует из существующего поведения, при котором деплой прерывается на первой ошибке. Назовите финальную фазу-сводку post-deploy, и она естественно получит это свойство.

Используйте untracked: true на фазе post-deploy, чтобы подавить системные сообщения о шагах (билтины сами производят вывод через свой уровень сообщений):

- name: post-deploy
description: Post-deploy summary
untracked: true
steps:
- name: info
type: dwe
cmd: "info"
- name: success
type: builtin
cmd: message
with:
level: success
text: Deploy completed successfully

В deploy.yml фаза с deploy_services: true является плейсхолдером. CLI заменяет её встраиваемыми пайплайнами сервисов на этапе выполнения, упорядочивая по зависимостям (поле after: в workspace/services/<name>/deploy.yml каждого сервиса). Включены только активные сервисы.

phases:
- name: services
deploy_services: true
description: Deploy all enabled services

По умолчанию dwe deploy run отслеживает исход и хеш каждого выполненного шага в .dwe/deploy/state.yml. На следующем запуске деплоя шаги, успешно завершившиеся с неизменным action_hash, пропускаются (если у них нет действия check:, которое всегда выполняется для повторной проверки идемпотентности).

Это делает деплои идемпотентными: повторный запуск неизменённого проекта быстр (неизменённые шаги пропускаются), а правка тела шага автоматически перезапускает его. Правки в конфигурационных файлах сервисов (workspace/services/<name>/service.yml) или в конфигах деплоя (workspace/deploy.yml, workspace/services/<name>/deploy.yml) инвалидируют затронутую область и заставляют эти шаги выполниться заново.

Ключевые модели поведения:

  • Изменение хеша шага → шаг перезапускается
  • Изменение конфига сервиса → все шаги сервиса перезапускаются
  • Изменение конфига проекта → все шаги уровня проекта перезапускаются
  • Есть действие check: → шаг всегда выполняется (даже если хеш совпадает), чтобы check повторно проверил идемпотентность
  • Есть files_gate: state: missing → пропуск по журналу обходится, а гейт переоценивается на каждом деплое (паттерн «продьюсер»: удаление артефакта должно повторно запустить производство независимо от содержимого журнала)
  • Есть files_gate: state: readable → пропуск по журналу учитывается первым, как у любого другого шага; гейт срабатывает только тогда, когда журнал иначе позволил бы шагу выполниться (паттерн «консьюмер»: деструктивные потребители остаются идемпотентными). Используйте явный check:, чтобы принудительно переоценивать на каждом запуске
  • В обоих случаях журнал фиксирует шаг для аудита и отображения статуса через step_hash, который включает конфигурацию гейта — поэтому изменение гейта инвалидирует записанный хеш и перезапускает шаг
  • Предыдущий шаг провалился → шаг перезапускается на следующем деплое (позволяет --resume продолжить с места падения)

Используйте dwe deploy state show для инспекции журнала, dwe deploy state clear для его сброса и dwe deploy state repair для починки повреждённых агрегатов.

Полные сведения о хешировании, решениях о пропуске и восстановлении после падений посреди деплоя см. в state/index.md.

  • Типы исполнения шаговshell, dwe, command, builtin; различие билтина cmd: shell и шага type: shell
  • Доступные билтины — каждый билтин с входами и примерами; внутренние билтины движка
  • Условия — семантика when:, check: и files_gate:
  • Примеры — оркестратор, отдельный сервис, infra-after:, параллельные группы, переопределения под-шагов в воркфлоу, типичные ловушки
  • dwe deploy plan — показать разрешённый пайплайн (с встраиваемыми фазами сервисов). --format table (по умолчанию) / --format shell, либо --output json для машиночитаемой формы: {service?, phases[]}, где каждая фаза несёт name / service / description / when и упорядоченный steps[] из {name, type, cmd, unresolved[], description, when, files_gate, check, continue_on_error, untracked, parallel{max_concurrent, fail_fast, steps[]}}. --output json имеет приоритет над --format.
  • dwe deploy run — выполнить пайплайн деплоя с отслеживанием состояния
  • dwe deploy state show — инспекция журнала состояния деплоя
  • dwe deploy state clear — сброс состояния деплоя
  • dwe deploy state repair — пересборка агрегатов состояния
  • dwe reset plan — показать пайплайн сброса
  • dwe reset run [--yes] — выполнить пайплайн сброса
  • См. также lifecycle.yml — пайплайны run / stop переиспользуют ту же грамматику фаз/шагов с опциональной пробой обновлений и хук-фазами.