lifecycle.yml
Run / stop pipeline declarations driving dwe run, dwe stop, and dwe restart.
Contents
Section titled “Contents”- Purpose
- Pipeline shape
- Structure
- Self-update probe
run.show_info/run.final_messagestop.final_messagelog(file logging)- Hook phases
- Minimal example
- Validation
- Parallel step groups
- Common pitfalls
- Related commands
Purpose
Section titled “Purpose”workspace/lifecycle.yml declares two pipelines:
run:— executed bydwe run(anddwe restart, after stop). Wraps the standarddocker up+docker waitsequence with optional pre/post hook phases. The self-update probe is configured separately in the top-levelupdate:block, not here.stop:— executed bydwe stop(and the first half ofdwe restart). Wrapsdocker downwith optional pre/post hook phases.
This file is loaded on its own and is not part of the 3-layer config merge.
The file is optional for all commands that use it.
When lifecycle.yml is absent or a section is absent, DWE substitutes a built-in default pipeline and prints one info line to stderr: Using built-in default <run|stop> pipeline (override with workspace/lifecycle.yml). The info line is suppressed in --output json mode.
Default run: pipeline (fires when lifecycle.yml is absent or has no run: section):
| Field | Value |
|---|---|
show_info | true |
final_message | Project is ready for work! |
| Phases | Single start phase: one type: dwe step with cmd: "docker up --wait" |
Default stop: pipeline (fires when lifecycle.yml is absent or has no stop: section):
| Field | Value |
|---|---|
final_message | Project is stopped. Have a nice day! |
| Phases | Auto-reap phase (see below) + single stop phase: one type: dwe step with cmd: "docker down" |
Whenever a stop: pipeline runs (default or user-defined), the _auto_reap_daemons phase is prepended automatically; it has no opt-out and is visible in plan output for transparency. It stops any background daemons started via type: daemon commands.
dwe docker up and dwe docker down are thin Docker Compose passthroughs and never use this pipeline; raw docker compose stop / restart remain accessible via dwe docker stop / dwe docker restart.
Pipeline shape
Section titled “Pipeline shape”flowchart LR
subgraph run["dwe run"]
direction LR
U[update probe] --> PRE[pre hooks] --> UP[docker up] --> WAIT[docker wait] --> POST[post hooks] --> INFO[info] --> MSG1[final_message]
end
subgraph stop["dwe stop"]
direction LR
SPRE[pre hooks] --> DOWN[docker down] --> SPOST[post hooks] --> MSG2[final_message]
end
subgraph restart["dwe restart"]
direction LR
R1[stop pipeline] --> R2[run pipeline<br/>--no-update]
end
docker up is issued as a single type: dwe step with cmd: "docker up --wait" inside the start phase; the --wait flag does the health waiting inline (no separate docker_wait_healthy step). It is not magical — the pipeline executor invokes it like any other step, so it picks up policy from docker.yml.
Structure
Section titled “Structure”run: show_info: true final_message: "Project is ready for work!" log: false # tee status + child stdout/stderr to .dwe/logs/run.log phases: - name: <phase> description: <text> when: # optional: typed condition (see deploy/conditions.md) type: builtin|shell|template cmd: <string> expr: <string> steps: - name: <step> type: shell|dwe|command|builtin cmd: <value> with: # optional: parameters key: value
stop: final_message: "Project is stopped. Have a nice day!" log: false # tee status + child stdout/stderr to .dwe/logs/stop.log phases: - name: <phase> description: <text> when: # optional: typed condition type: builtin|shell|template cmd: <string> expr: <string> steps: - name: <step> type: shell|dwe|command|builtin cmd: <value> with: # optional: parameters key: valuePhases and steps use the same shape as deploy.yml: name, description, when, untracked, steps[], plus per-step type / cmd / with, when, check, files_gate, continue_on_error. See the deploy reference for the complete step grammar, including files_gate: (pre-condition for files).
deploy_services: true is not allowed in lifecycle pipelines.
Self-update probe
Section titled “Self-update probe”The optional self-update probe runs before any phase. It can fetch from the upstream remote, detect drift, and (with consent) pull --ff-only. A successful pull triggers in-process reload of DweConfig, LifecycleConfig, and the command registry before phases execute.
The probe is driven by the formalized top-level update: block in workspace.yml / local.yml (mode: on | off), which participates in the 3-layer merge. Enabling update is a one-liner that does not blank run.phases.
Runtime precedence at dwe run: --no-update flag > --update <mode> flag > update.mode from the merged config. See the update: block reference and git integration → update probe for full behaviour.
run.show_info / run.final_message
Section titled “run.show_info / run.final_message”| Field | Type | Default | Description |
|---|---|---|---|
show_info | bool | false | Append a dwe info render after the last phase. |
final_message | string | Project is ready for work! | Success message printed at the very end. |
Required service deployment gate
Section titled “Required service deployment gate”dwe run automatically gates on required services being deployed. Before the run pipeline starts, the command checks that all tracked services (those appearing in the resolved deploy plan) have status: deployed in the state file.
If any tracked service is not yet deployed, dwe run exits with an error: “run dwe deploy run first”. This prevents running against a partially-initialized environment — bypassing the gate would just hand docker compose up a service whose volumes/configs/database have never been provisioned, and the run would fail almost immediately with an unrelated error. Always deploy first.
For more details, see state/index.md.
stop.final_message
Section titled “stop.final_message”| Field | Type | Default | Description |
|---|---|---|---|
final_message | string | Project is stopped. Have a nice day! | Success message printed at the very end. |
log (file logging)
Section titled “log (file logging)”Top-level field on both run: and stop:. Defaults to false for lifecycle pipelines (in contrast to deploy.yml, where the default is true).
When enabled, dwe status messages and child-process stdout/stderr are teed to .dwe/logs/<name>.log (with ANSI codes stripped) — .dwe/logs/run.log for run, .dwe/logs/stop.log for stop.
run: log: true # tee to .dwe/logs/run.logHook phases
Section titled “Hook phases”Hook phases are conventional names (pre / post) used to wrap the standard start/stop work. Add continue_on_error: true on each step so a failed hook does not abort the main lifecycle:
run: phases: - name: pre description: Before-run hooks (continue on failure) steps: - name: before-run type: command cmd: project.before-run continue_on_error: true
- name: start description: Start containers and wait for health steps: - name: up type: dwe cmd: "docker up" - name: wait type: builtin cmd: docker_wait_healthy
- name: post description: After-run hooks (continue on failure) steps: - name: after-run type: command cmd: project.after-run continue_on_error: truecontinue_on_error: true causes the failure to be reported via FailStep (red ✗), but execution moves to the next step and the post-step check is not evaluated.
Minimal example
Section titled “Minimal example”run: show_info: true final_message: "Project is ready for work!" phases: - name: start description: Start containers and wait for health steps: - name: up type: dwe cmd: "docker up" - name: wait type: builtin cmd: docker_wait_healthy
stop: final_message: "Project is stopped. Have a nice day!" phases: - name: stop description: Stop and remove containers steps: - name: down type: dwe cmd: "docker down"Validation
Section titled “Validation”On load, the file is checked for:
- Each step in
run.phasesandstop.phaseshas atype:field with one ofshell,dwe,command,builtin. - A
run.updateblock is rejected — the self-update probe is configured in the top-levelupdate:block. The strictlifecycle.ymldecoder hard-errors on the unknownupdatekey underrun:. deploy_services: trueis rejected (only valid indeploy.yml).final_messageandlogare normalized to defaults when absent.
Parallel step groups
Section titled “Parallel step groups”Lifecycle phases use the same parallel: step-group container as deploy.yml. A step may declare parallel: { max_concurrent, fail_fast, steps } instead of a leaf body, and the inner sub-steps run concurrently with the same cancellation, journal, and reporter semantics. See deploy → Parallel step groups for the schema, defaults, validation rules, and execution model.
Common pitfalls
Section titled “Common pitfalls”- Forgetting
continue_on_error: trueon hook steps — without it, a failing pre-stop hook aborts the entire stop sequence and containers are never stopped. - Putting
update:underrun:— the self-update probe is configured in the top-levelupdate:block inworkspace.yml/local.yml, not inlifecycle.yml. Arun.updateblock is rejected at load time. - Adding
deploy_servicesphases — they are deploy-only. Lifecycle pipelines call services viatype: commandreferences instead. - Editing
lifecycle.ymlto use directdocker composecalls — the public API istype: dwewithcmd: "docker up". Directdocker composecalls bypass policy fromdocker.yml.
Related commands
Section titled “Related commands”dwe run— execute the run pipeline (with optional update probe)dwe run --no-update— skip the update probedwe run --update <mode>— override the configured modedwe stop— execute the stop pipelinedwe restart—stop, thenrun --no-updatedwe docker up/dwe docker down— raw Docker Compose passthrough (does not use this pipeline)