Skip to content

CI integration

The CLI is built to slot into CI. It uses deterministic exit codes, it emits machine-readable event streams, and it has no interactive dependencies.

OutcomeCode
succeeded0
failed / partial1
usage or parse error2
internal error3
cancelled130

A multi-target run exits with the worst of the runs’ codes. Internal error ranks above failed or partial, which ranks above cancelled, which ranks above success. This gives you one gate for a whole batch.

The global --json flag emits machine-readable output. For run it is an NDJSON event stream. Each line holds one kind-tagged execution event, and a final summary line caps the stream:

Terminal window
flow run flow.flow --json | jq 'select(.kind=="summary")'

These are the same execution events the desktop renders live and that Flow Server streams over SSE. All three surfaces share one event vocabulary.

flow fmt --check exits non-zero when a .flow file is not in canonical form. Wire it in next to your linters:

Terminal window
flow validate pipeline.flow # parse + static checks; errors as path:line:col
flow fmt --check pipeline.flow # canonical-form gate
Terminal window
flow run smoke.flow integration.flow deploy-check.flow --json

Every target runs at once on the shared core, and each one has its own execution id. Each JSON event carries its executionId, and each summary line carries its target.

  • Set FLOW_STUDIO_DIR to a temp path to isolate a CI run from any local state.
  • Headless runners bypass the interactive destructive-action confirmation gate. Review flows before you wire them into unattended pipelines, and prefer pre-apply verification warnings during authoring.
  • flow generate --auto exits 0 only on convergence. That makes “can the agent fix the flow” itself a gateable check.
  • Tracing goes to stderr (FLOW_LOG / RUST_LOG), so stdout stays clean for results and --json streams.