Step execution types
Every leaf pipeline step declares a type: that selects how its cmd: is executed.
Contents
Section titled “Contents”type: shell
Section titled “type: shell”Executes a shell command via sh -c. Full shell semantics apply: environment variable expansion, globbing, pipes, redirection, and &&/|| operators all work as expected.
- name: chmod-scripts type: shell cmd: chmod +x scripts/deploy.shcmd: shell (builtin) vs type: shell (step)
Section titled “cmd: shell (builtin) vs type: shell (step)”The shell builtin (cmd: shell) is distinct from the step execution type (type: shell). Both execute shell commands, but with different portability guarantees:
Step type: type: shell — Uses the project’s configured shell (via config.ShellBin) for maximum flexibility. If the project has set a custom shell binary (e.g., zsh instead of sh), step bodies use that shell.
- name: run-with-project-shell type: shell cmd: some-zsh-specific-feature-hereBuiltin: cmd: shell — Uses POSIX-portable hardcoded sh -c for maximum predictability. Used in two contexts:
- As a step body (less common):
- name: check-docker-login type: builtin cmd: shell with: cmd: docker info | grep -q ghcr.io timeout: 10s- As a pre/post-condition (common in deploy and validate):
- name: copy-configs type: builtin cmd: service_configs_copy # ... when: type: shell cmd: "test -f templates/config.default"
check: type: builtin cmd: shell with: cmd: "test -f services/main/configs/app.conf"Both usages ensure that conditions evaluate portably across CI systems, container runtimes, and developer shells, regardless of the project’s config.ShellBin setting. See validate.yml for the full cmd: shell builtin documentation.
Working directory. Both the type: shell step body and the cmd: shell builtin run with their working directory set to the project root — the same base when: conditions and the file_exists builtin use. A relative path in a step body therefore names the same file as the relative path in the check: guarding it, regardless of the subdirectory dwe was invoked from.
Timeout. The cmd: shell builtin takes an optional timeout: (default 10s). timeout: "0" means unbounded, matching a step’s own timeout: convention — this is what lets a derived check: auto keep the unbounded posture of the when: it inverts. A negative duration ("-5s") is rejected, exactly as the step-level timeout: is — 0 is the only unbounded spelling. type: shell step bodies have no builtin timeout; bound them with the step-level timeout: field.
type: dwe
Section titled “type: dwe”Invokes a DWE CLI subcommand. The binary path is resolved automatically.
- name: up type: dwe cmd: "docker up"
- name: info type: dwe cmd: "info"
- name: render-ide type: dwe cmd: "render ide main"type: command
Section titled “type: command”Dispatches a declarative command by ID from the command registry (workspace/commands/).
- name: composer-install type: command cmd: services.main.composer-install
- name: db-create type: command cmd: services.main.db.create with: database: laravel_testtype: builtin
Section titled “type: builtin”Executes an engine-internal Go function. Builtins run in-process and have access to the full config. The same registry is reachable from declarative commands via type: builtin in commands/ — pipelines and commands share one set of builtins.
- name: create-dirs type: builtin cmd: service_dirs_ensure with: service: main mode: skip
- name: success-msg type: builtin cmd: message with: level: success text: "Deploy completed"See Available builtins for the full registry and parameter reference.
Predicate builtins as step bodies (assertion semantics)
Section titled “Predicate builtins as step bodies (assertion semantics)”Most builtins are actions (they do something). Some are predicates — they answer a yes/no question about the world (file_exists, executable_in_path, tcp_reachable, http_check, containers_running, env_keys_present, config_keys_present, and the shell builtin). A predicate may be used as a step body, where it behaves as an assertion:
- The check passes → the step succeeds.
- The check fails → the step fails with the predicate’s own message, halting the pipeline.
- name: assert-seed-present type: builtin cmd: file_exists with: path: .dwe/seed.sqlAssertion steps are always re-run — deploy’s “already up-to-date” gate and the per-step action-hash skip never skip a predicate-body step (the same treatment check: steps receive), because an assertion has no meaningful cached result. A when: guard still applies: a predicate-body step whose when: is false is skipped without asserting.
See the predicate-as-body preamble in the builtins reference for the full list and rationale.