Авторство проектных команд
README проекта обрастает командами, которые копируют и вставляют вручную: «запусти это, чтобы засидить базу», «запусти это, чтобы пересобрать фронт», «запусти это, когда очередь барахлит». Это руководство показывает, как заменить такие сниппеты на полноценные команды dwe <id>: вся команда сможет находить их через dwe commands, запускать через dwe cmd <id> и собирать из них более крупные пайплайны.
Полная схема — в ../reference/config/commands/index.md; эта страница — про три типа, к которым вы будете обращаться чаще всего (shell, service_exec, workflow), и про директивы, делающие их безопасными в продакшене.
Раскладка файлов и ID команд
Заголовок раздела «Раскладка файлов и ID команд»Команды живут под workspace/commands/. Путь каталога становится ID команды, где сегменты разделены точками. Базовое имя файла — это листовой сегмент, а каждый ключ в commands: файла добавляет ещё одну точку.
workspace/commands/├── db.yml # группа: db└── db/ └── seed.yml # группа: db.seedКоманда с ключом default внутри workspace/commands/db/seed.yml была бы dwe cmd db.seed.default. Чаще ядро действия кладут под описательный ключ:
group: title: Database seeding
commands: run: # полный ID: db.seed.run type: shell description: Seed the database with development fixtures cmd: | "$DWE_BIN" shell app -c "php artisan db:seed --class=DevSeeder"Запуск — dwe cmd db.seed.run, видна в dwe commands list или в интерактивном браузере по dwe commands.
Эмпирическое правило: основные команды группы держите в одном файле, названном по имени группы; разбивайте на поддиректорию только тогда, когда команд набирается достаточно для выделения логических подгрупп.
type: shell — простейший случай
Заголовок раздела «type: shell — простейший случай»type: shell запускает команду на хосте через sh -c. Используйте для git-операций, host-side build-шагов или любого one-liner, которому не нужно быть внутри контейнера.
commands: format: type: shell description: Run gofmt over the whole repo cmd: gofmt -w .cmd: — это строка, которую отдают в sh -c, так что полная семантика шелла (пайпы, редиректы, env-расширение) работает. Если хотите обойти шелл целиком — используйте argv::
commands: commit-config: type: shell description: Commit a generated config file argv: - git - commit - -m - "chore: regen config" - generated.ymlcmd: и argv: взаимоисключающие.
Контракт env у shell
Заголовок раздела «Контракт env у shell»Каждый подпроцесс type: shell наследует три экспортированные переменные, чтобы дотянуться до того же compose-проекта, которым управляет DWE, без повторного его обнаружения:
| Переменная | Значение |
|---|---|
DWE_BIN | Абсолютный путь к запущенному бинарю dwe |
COMPOSE_PROJECT_NAME | Активное имя compose-проекта |
COMPOSE_FILE | Список активных оверлейных путей (абсолютных), склеенных двоеточием |
Используйте "$DWE_BIN" вместо ./bin/dwe — это делает команды переносимыми между машинами:
commands: warm-cache: type: shell cmd: | "$DWE_BIN" shell app -c "php artisan cache:warm"COMPOSE_PROJECT_NAME и COMPOSE_FILE позволяют вызовам docker compose ... внутри cmd: подхватывать оверлеи DWE без флагов -p / -f.
type: service_exec — запуск внутри контейнера
Заголовок раздела «type: service_exec — запуск внутри контейнера»type: service_exec запускает команду внутри уже работающего контейнера через docker compose exec. Используйте для операций уровня приложения (artisan, manage.py, rails, mix), которые должны идти против живого контейнера.
commands: db.create: type: service_exec description: Create a database in the db container service: db mode: exec-or-run params: database: type: string required: true pattern: ^[a-zA-Z0-9_-]+$ env: MYSQL_PWD: "${vars.db.password}" cmd: "mariadb -u${vars.db.user} -e 'CREATE DATABASE IF NOT EXISTS `${param.database}`;'"Ключевые поля:
-
service:— имя compose-сервиса. -
mode:— что делать, если контейнер не запущен:exec-or-fail(по умолчанию) — отказ с действенной ошибкой и подсказкойdwe docker up <svc>.exec-or-run— фолбэк на свежийdocker compose run --rm-контейнер с предупреждением об эфемерном запуске.exec— голыйdocker compose exec; docker эмитит свою ошибку, если контейнер лёг.run— всегда поднимать свежий контейнер.
Берите
exec-or-failдля инструментов, зависящих от постоянного состояния (БД, app-серверы);exec-or-run— только когда инструмент честно работает one-off (composer install на свежем чек-ауте и т.п.). -
user:—currentзапускает от UID:GID хоста (для команд, пишущих в bind-mount);root— для привилегированных контейнерных операций; либо литералname/1000/1000:1000. Опустите — наследуетсяcli.userсервиса. -
workdir_from:— dot-путь в собранный конфиг (например,services.main.work_dir_internal). Предпочтительнее жёсткогоworkdir:, потому что это позволяет оверрайдам изlocal.ymlдоходить до команд.
Для one-off инструментов, которым всегда нужен свежий контейнер (artisan tinker, php -a, irb), берите type: service_run — те же поля, но всегда docker compose run --rm.
Полный справочник: ../reference/config/commands/types.md#type-service_exec.
type: workflow — компонуем несколько команд
Заголовок раздела «type: workflow — компонуем несколько команд»Workflow сшивает существующие команды в одну именованную последовательность. Используйте, когда трижды-четырежды подряд запускаете одни и те же команды или когда одно пользовательское действие («bootstrap», «reset-and-reseed») естественно распадается на несколько шагов.
commands: bootstrap: type: workflow description: Full bootstrap — db, deps, migrate, seed steps: - command: db.start - command: db.create with: database: "${vars.db.database}" - command: composer-install - command: migrate - command: db.seed.run when: "{{ if .Params.seed }}1{{ else }}0{{ end }}" continue_on_error: true params: seed: type: bool default: trueКаждый шаг — это один из трёх вариантов (взаимоисключающие):
command: <id>— вызов другой команды.with:переопределяет её params;when:пропускает шаг, когда выражение ложно;continue_on_error: trueпревращает падение в предупреждение вместо прерывания.confirm: <text>— спросить пользователя перед продолжением. Обходится через--yesиDWE_NONINTERACTIVE=1.parallel:— разветвить группу листовых command-шагов параллельно. Полезно для «запусти composer install в каждом app-сервисе».
Выражения when:
Заголовок раздела «Выражения when:»when: шага сначала рендерится, затем классифицируется:
| Форма | Пример | Заметки |
|---|---|---|
| Boolean-литерал | "true", "1", "" | Быстрый путь после рендера |
| Builtin-предикат | file-missing services/main/vendor/autoload.php | dir-exists, dir-missing, dir-empty, dir-not-empty, file-exists, file-missing; пути относительны корня проекта |
| Shell-команда | cmd: test ! -d services/main/vendor | Прогоняется через sh -c; exit 0 = true |
Предпочитайте builtin-предикаты, когда они подходят — они дешевле и не плодят шелл.
Полный справочник: ../reference/config/commands/types.md#type-workflow.
Params: типизированные входы
Заголовок раздела «Params: типизированные входы»Каждый тип команды может объявить params: — типизированные входы, которые пользователь подаёт через --set key=value на CLI или которые передаёт workflow/pipeline-шаг через with:.
commands: db.create: type: service_exec service: db params: database: type: string # string (по умолчанию) | bool | int | path description: Database name to create required: true default: "app" # литеральный фолбэк default_from: vars.db.database # dot-путь в собранный конфиг (предпочтительнее) env: DB_NAME # экспонирует значение как $DB_NAME pattern: ^[a-zA-Z0-9_-]+$ # якорный regex (только string/path) env: MYSQL_PWD: "${vars.db.password}" cmd: "mariadb -u${vars.db.user} -e 'CREATE DATABASE `${param.database}`;'"Порядок разрешения, сверху вниз:
- Значение от вызывающего (
--set database=fooилиwith: { database: foo }). default_from— dot-путь в собранный конфиг DWE. Пустой результат считается отсутствующим.- Литеральный
default:. - Если всё ещё пусто и
required: true— ошибка.
Правило default_from позволяет оверрайдам из local.yml доходить до команд без того, чтобы каждый разработчик переписывал литеральный default. Это тот же паттерн «конфиг побеждает, код даёт safety net», что и в других местах DWE.
Используйте разрешённое значение как ${param.<name>} в cmd:, argv:, env:, workdir:, confirmation_text: и путях к файлам.
Чтобы показать params в интерактивном браузере как дружелюбную форму (dropdown-ы, multi-select, confirm-виджеты), объявите widget: и options: — см. param widgets.
Подтверждение и --yes
Заголовок раздела «Подтверждение и --yes»Любая команда может требовать подтверждение перед запуском:
commands: db.drop: type: service_exec description: Drop a database (irreversible!) confirmation: true confirmation_text: "Drop database `${param.database}`?" service: db params: database: required: true default_from: vars.db.database env: MYSQL_PWD: "${vars.db.password}" cmd: "mariadb -u${vars.db.user} -e 'DROP DATABASE IF EXISTS `${param.database}`;'"confirmation_text: поддерживает ${...}-шаблонизатор, чтобы можно было показать значения, над которыми пользователь сейчас собирается выполнить действие. Промт обходится в трёх случаях:
- Пользователь передал
--yes/-yна CLI. - Команда выполняется как sub-step workflow под родителем, запущенным с
--yes. - Не-TTY stdin при
CI=1(авто-подтверждается через plain Y/n фолбэк).
Для скриптов канонический идиом: dwe cmd db.drop --set database=app --yes.
Уведомления
Заголовок раздела «Уведомления»notify: true подключает команду к desktop-уведомлению по завершении (успех или провал). Уведомление срабатывает только когда команда — верхнеуровневый вызов; workflow и пайплайны не пробрасывают вложенный notify: пользователю.
commands: db.import: type: shell notify: true cmd: | "$DWE_BIN" shell app -c "php artisan db:import ${param.dump}"Используйте для долгих «запустил и переключился в другое окно» команд (большие импорты, полные bootstrap-ы, pack/unpack снапшота). Для команд короче секунды опускайте — десктоп-попапы для мгновенных операций лишь создают шум.
notify: true на type: daemon — это ошибка валидатора (у демонов нет события завершения), на sub-step внутри parallel: — info-диагностика (runtime его всё равно подавляет).
Полный справочник: ../reference/config/notifications.md.
Видимость: private: и hide:
Заголовок раздела «Видимость: private: и hide:»По умолчанию каждая команда показывается в dwe commands list, в интерактивном браузере и в tab-completion. Две директивы их скрывают:
-
private: true— статическое намерение автора. Команда исключена изcommands list, браузера и прямого вызоваdwe cmd <id>, но остаётся вызываемой из workflow и пайплайнов. Используйте для «step»-команд, которые не должны запускаться напрямую.commands:db.up:type: dweprivate: true # используется только внутри workflow db.startcmd: "docker up db" -
hide:— runtime-условие. Тот же синтаксис выражения, что и уwhen:workflow-шага. Команда видна, когда выражение falsy, и исчезает, когда truthy. Типичное использование — привязать команду к включённости сервиса.commands:db.engine.reset:type: shellhide: '{{ eq (index .services "db" "engine") "sqlite" }}'cmd: db reset --enginehide:на блокеgroup:скрывает всю группу и потомков.
private: подходит для «внутренней проводки» — атомарных шагов workflow, не предназначенных к показу. hide: подходит для «релевантно только в некоторых конфигурациях» — команды, имеющей смысл только при одном из движков сервиса или только при включённом опциональном сервисе.
Перекрёстные ссылки
Заголовок раздела «Перекрёстные ссылки»../reference/config/commands/index.md— полная структура файла и жизненный цикл выполнения.../reference/config/commands/types.md— каждый тип команды (shell,dwe,script,service_exec,service_run,workflow,builtin,daemon) со всеми type-specific полями.../reference/config/commands/directives.md— каждая общая директива на одной странице.../reference/config/commands/templating.md— полная таблица резолвера${...}и{{ ... }}.background-daemons.md— формаtype: daemonдля долгоживущих фоновых процессов.