Results and routing #
An action's stdout and stderr are its log. What happens next is decided by its
result: by default its exit code, or a JSON file it writes to $CI_RESULT.
The schema is
run.rtw.ci.step.result; the
rationale is in DESIGN.md §7–8.
Without a result file #
Plain recipes need nothing: exit 0 is a success, anything else a failure that stops the run.
The result file #
{
"version": 1,
"status": "failure",
"summary": "mx99 /healthz 503 for 4m",
"next": [
{ "action": "rollback", "prompt": "Roll back?", "then": "stop" },
{ "action": "health-check", "label": "Check again" }
],
"attachments": [{ "label": "health probes", "path": "health.ndjson", "type": "ndjson" }],
"outputs": { "hosts": ["mx99.eu.sovrn.at"] }
}
| Field | Meaning |
|---|---|
version |
Required. 1 |
status |
Required. success, warning or failure. A warning routes like a success but shows as one on the run page |
summary |
Markdown shown on the run page, at gates and in notifications |
next |
What runs next. Omitted: default routing. "stop": end the run. Otherwise an action name, one option, or a list of options |
gated |
Ask a person before taking next, even if the action has no gate of its own |
attachments |
Files under $CI_ARTIFACTS shown on the run page: { label, path, type }, where path is relative to $CI_ARTIFACTS and type is text, markdown, ndjson or json |
outputs |
An object merged into $CI_STATE/outputs.json, for later actions (and manual child runs) to read |
An option in next:
| Field | Meaning |
|---|---|
action |
Required. A declared action, on the happy path or not |
then |
After that action succeeds: continue (carry on along the happy path) or stop. Default: continue for happy-path steps, stop for anything else |
label |
Button label at the gate |
prompt |
Question for the person |
inputs |
An object given to the action as the JSON file at $CI_INPUTS |
A result that is not valid JSON, doesn't match the schema, names an undeclared
action or a path outside $CI_ARTIFACTS, or claims success with a non-zero
exit, is a failure, and the error is shown on the run.
ci-result #
ci-result is on PATH in every action and writes a valid result:
ci-result ok|warn|fail [flags]
| Flag | Meaning |
|---|---|
--summary TEXT |
Summary; repeat for more lines |
--summary-file FILE |
Read the summary from a file (- for stdin) |
--next ACTION |
Route to an action; repeat for a choice. The next four flags apply to the most recent --next |
--then continue|stop |
That option's then |
--label TEXT, --prompt TEXT |
That option's label and prompt |
--input KEY=VALUE, --inputs-json JSON |
That option's inputs |
--stop |
End the run after this action (not with --next) |
--gated |
Ask a person before taking --next |
--attach [LABEL=]PATH |
Attach a file; copied into $CI_ARTIFACTS if it is elsewhere. The type comes from the extension (.md, .ndjson, .json, else text) |
--output KEY=VALUE, --outputs-json JSON |
Merge into $CI_STATE/outputs.json |
--stdout |
Print the result instead of writing $CI_RESULT |
A fail result is used whatever the exit code, so a recipe can write one and
then exit non-zero. An ok or warn result with a non-zero exit is a contract
error, so don't write success before the work is done.
Routing #
A run keeps a cursor on the happy path. After each action:
- No
next. On success or warning, a happy-path step moves on to the next step; any other action ends the run. On failure, the run ends. next: "stop". The run ends, as a success unless this result is a failure. This is how an action ends a run early on purpose ("Nothing to deploy").- One option. It runs straight away, unless its action declares a
gateor the result saysgated: true; then the run waits for a person. - Several options. Always a gate: the person picks one or dismisses.
Naming a happy-path step in next moves the cursor there: forward skips steps,
backward repeats them. After the chosen action succeeds, then: continue
resumes the happy path after the cursor and then: stop ends the run.
Rules worth remembering:
- Failure stops. Without a
next, a failed action ends the run. - Off-path actions stop after themselves. An action that isn't a step ends
the run when it succeeds, unless its option says
then: continueor its own result routes on. Nothing drifts back onto the happy path by accident. - Gates only get added. A result can ask for a gate (
gated, or a choice); it can never skip a gate an action declares. - At most 50 transitions per run, so a loop can't spin forever.
How a run ends #
| End | Status |
|---|---|
| Ran past the last step | success |
A result said next: "stop" |
success, or failure if that result was a failure |
A happy-path step with then: stop |
success if it was the last step, else failure |
An off-path action with then: stop ("diverted: stopped after rollback") |
failure, so a rolled-back run is never green |
A failure with no next, a contract error, the transition limit |
failure |
| A declared gate dismissed | cancelled |
| A gate raised by a result dismissed | that result's status (failure stays failure; success and warning become success) |
A gate expired, --until, Ctrl-C |
cancelled |
A manual action run on its own (a child run) ends with that action's own status.