Conditions and Actions
Typed conditions (when:) and typed actions (check: / step bodies) in pipelines.
Contents
Section titled “Contents”- Overview
- Typed conditions (
when:) - Typed actions (
check:and step bodies) check: auto— the inverse ofwhen:- Two
type: builtinregistries - Workflow conditions (string-based, separate)
- Related documentation
Overview
Section titled “Overview”Conditions (when:) are pre-conditions evaluated before a phase or step runs. They return a boolean: true = proceed, false = skip.
Actions are payloads that execute code. When used as check: (post-action), their success/failure determines whether the step passed or failed.
The pipeline system uses typed forms for both — a type: field dispatches to different evaluators. Workflow steps (inside command definitions) use a separate string-based when: form documented in commands/.
Pipeline steps (typed): when: { type: builtin|shell|template, cmd: ..., expr: ... } check: { type: shell|dwe|command|builtin, cmd: ..., with: ... } check: auto # the logical inverse of when:
Workflow steps (string-based — separate, not covered here): when: "dir-empty path" | "{{ ... }}" | "cmd: ..." command: <id>Typed conditions (when:)
Section titled “Typed conditions (when:)”when: on a phase or pipeline step is a typed condition with three forms. It is evaluated before the phase/step runs; a falsy result skips it.
type: builtin — predicates
Section titled “type: builtin — predicates”Builtin conditions test filesystem state using the predicate registry. Predicates are distinct from engine builtins (like service_configs_copy) — they live in a separate namespace and cannot be used in check: actions.
when: type: builtin cmd: "dir-empty services/main/src"Available predicates (path is project-root-relative):
| Predicate | True when |
|---|---|
dir-exists <path> | path is an existing directory |
dir-missing <path> | path is missing or not a directory |
dir-empty <path> | path is missing or has no entries |
dir-not-empty <path> | path is a directory with at least one entry |
file-exists <path> | path is an existing regular file |
file-missing <path> | path is missing or not a regular file |
generated-missing <svc> <field> | the <field> value is absent from the generated-value store (.dwe/generated.yml), or the store file is missing |
Unlike the other predicates, generated-missing takes two sub-arguments — a service name and a generated-field name — rather than a path. It reads the durable per-service store at .dwe/generated.yml and is used to gate a service’s secret-generation step so it runs only on the first deploy (when no value has been harvested yet). See services/fields.md for the generated: declaration and render/config.md for the harvest/replay flow.
Portability: Predicates are evaluated through hardcoded sh -c (not the project’s configured shell binary) to ensure POSIX portability and consistency regardless of the project’s shell choice.
type: shell — shell commands
Section titled “type: shell — shell commands”Shell conditions execute a command and test its exit code: exit 0 = true, non-zero = false.
when: type: shell cmd: "test -f services/main/src/vendor/autoload.php"Full shell semantics apply: pipes, redirection, operators, etc. Like predicates, shell conditions use hardcoded sh -c for portability.
type: template — Go templates
Section titled “type: template — Go templates”Template conditions are evaluated at plan time using Go text/template syntax. They do not support check: in the same step (no side effects before execution).
when: type: template expr: "{{ .Services.second.Enabled }}"Template conditions are purely for idempotency checks known at plan time:
- name: setup when: type: template expr: "{{ not .Services.database.Enabled }}" steps: []The render context includes the full resolved project config, so you can reach any configuration value. See Templates for the template expression syntax and helper reference.
Typed actions (check: and step bodies)
Section titled “Typed actions (check: and step bodies)”Actions are executable payloads — the same type: shell|dwe|command|builtin shape used in step bodies. When used as a check: post-action, the action’s success/failure determines the step’s success/failure.
- name: copy-configs type: builtin cmd: service_configs_copy with: service: main mode: replace check: type: builtin cmd: service_configs_check with: service: mainActions support four executor types:
| Type | Executor | Example |
|---|---|---|
shell | sh -c | type: shell, cmd: "test -f file.txt" |
dwe | DWE CLI | type: dwe, cmd: "docker up" |
command | Command registry | type: command, cmd: "services.main.migrate" |
builtin | Engine builtin | type: builtin, cmd: "service_configs_check" |
See deploy/conditions.md for the full action reference and the semantics of check: failures under continue_on_error.
check: auto — the inverse of when:
Section titled “check: auto — the inverse of when:”Besides the mapping form, check: accepts one scalar: auto. It resolves to the logical inverse of the step’s own when:, for the common step whose “should I run” and “am I already done” are the same predicate read in opposite directions.
- name: clone-source type: shell cmd: "git clone ${vars.source.repo} services/backend/src" when: type: shell cmd: "[ ! -e services/backend/src/.git ]" check: auto # ≡ check: {type: builtin, cmd: shell, with: {cmd: "! ( [ ! -e … ] )"}}It applies only to when: {type: shell}. The other two forms are load-time errors:
type: builtin— the twotype: builtinnamespaces below are disjoint.dir-emptyis a predicate and has no counterpart in the action registry thatcheck:draws from, so there is no action that could express “NOTdir-empty foo”. (Negating by swapping in the “paired opposite” predicate name would also be wrong at the edges —dir-emptyanddir-not-emptyare not complements for a missing directory.)type: template— template conditions are evaluated at plan time and a false one removes the step entirely. Every step that survives to execution therefore hadwhen == true, so its inverse is always false and the derived check would always fail.
check: auto without a when: is rejected as well — there is nothing to invert. The inversion is a logical negation of the rendered command (! (\n<cmd>\n)), never a textual edit of it.
See deploy/conditions.md for the resolution details (shell, working directory, timeout) and the journal/config-hash consequences.
Two type: builtin registries
Section titled “Two type: builtin registries”The pipeline system has two separate type: builtin namespaces, disambiguated by YAML position:
- Predicates — used in
when: type: builtin. Filesystem-state tests likedir-empty,file-exists. - Engine builtins — used in step bodies and
check: type: builtin. Executable actions likeservice_configs_copy,service_configs_check,message.
Example of the distinction:
phases: - name: setup when: # when: uses the PREDICATE registry type: builtin cmd: "dir-empty src" steps: - name: copy type: builtin # step body uses the ENGINE BUILTIN registry cmd: service_configs_copy with: service: main - name: verify check: # check: uses the ENGINE BUILTIN registry type: builtin cmd: service_configs_check with: service: maindir-empty is not an engine builtin (not available as a step body or check). service_configs_copy is not a predicate (not available in when:).
Workflow conditions (string-based, separate)
Section titled “Workflow conditions (string-based, separate)”Workflow steps use a separate string-based condition mini-language. The full grammar is documented in commands/; this section only sketches the surface for context.
# Workflow (string-based, separate system)steps: - command: services.main.migrate when: "file-missing services/main/src/vendor/autoload.php"
- confirm: "Proceed?" when: "{{ if .Params.confirm }}1{{ else }}0{{ end }}"
- command: cleanup when: "cmd: test -d /tmp/workdir"Workflow conditions are classified by leading prefix ({{ ... }} → template, cmd: ... → shell command, otherwise → predicate). See commands/ for the full workflow grammar.
Related documentation
Section titled “Related documentation”- deploy — pipeline
when:andcheck:syntax with examples - lifecycle.md — lifecycle pipelines (same step/condition grammar as deploy)
- commands/ — command definitions (separate system; workflows keep string-based
when:) - Templates — Go template syntax, sprout helpers, render contexts