CI integration
dflux Runner gates CI pipelines with a deterministic exit code and optional machine-readable reports. This guide covers exit-code semantics, -output json|junit, useful flags, and snippet templates for common CI systems.
How to gate a pipeline
For one-shot CI, drive the CLI directly. No control plane, no local database, no web UI:
- Exit code —
run-flowexits on the flow verdict;run-suiteandcheckexit non-zero when something fails. Gate the job on$?. - Structured report — pass
-output jsonor-output junit(optionally-output-file <path>). The report is written before the process exits, so CI can upload the artifact and still fail the job on the exit code.
Exit codes
run-flow exits on the flow verdict (the same answer printed first in the report), not on the narrower all_passed flag:
| Exit code | Meaning |
|---|---|
0 | Verdict pass — the flow ran correctly |
1 | Verdict fail, or the run errored (json/junit reports still write first) |
2 | Usage error — bad flags or an unusable -output format |
3 | Verdict unsupported — the core does not implement this scenario. Not a failure. |
A strict gate treats only 0 as green. A permissive gate that already knows the core is incomplete can accept 0 or 3:
run-suite and check do not use the third code. run-suite exits 0 only when every cycle's AllPassed is true (a step's success_when can override that step's own pass/fail). check exits 1 if any transport probe failed, 2 on usage errors.
Machine-readable output
| Flag | Purpose |
|---|---|
-output text|json|junit | Result format (default text) |
-output-file <path> | Write the json/junit report to a file; the human summary still prints to stdout |
JUnit XML is suitable for GitLab reports.junit and similar consumers. JSON is the full engine result shape for custom assertions (latency fields, post-checks, event log).
Useful flags for CI
| Flag | Purpose |
|---|---|
-repetitions <n> | Number of flow starts (default 1) |
-rate <n> | Flow starts per second (0 = burst all at once) |
-timeout <dur> | Per-flow timeout (default 30s); raise it for slow cores |
-gen-subscriber | Synthesize a subscriber per UE in memory; skip a subscribers file |
-output json|junit | Structured report for artifacts / test views |
-output-file <path> | Write that report to a file (human summary still on stdout) |
-trace | TX/RX hex dumps for failed-run forensics — strip from green pipelines |
-metrics-port <n> | Optional Prometheus runner_* endpoint for the lifetime of the process |
There is no -db flag and no local report catalog — the process is stateless. For latency or path assertions, encode them as post-run checks in the flow or suite YAML so they feed the exit code. See the flow schema.
GitHub Actions example
The job fails when run-flow exits non-zero. Exit 3 (unsupported) is also non-zero, so a strict Actions job treats a missing-feature verdict as red unless the script maps it. if: always() keeps the artifact upload firing on red builds so the JUnit report is available for triage.
GitLab CI example
The job's pass/fail tracks the run-flow exit code. reports.junit feeds GitLab's test view from the same file.
Suites in CI
run-suite follows the same exit-code and -output contract. Use a suite when one logical pipeline check needs several flows in order — for example: provision PCC rules, run a load burst, then tear the rules down. Each step's workload lives in the suite YAML; run-suite has no suite-level -rate. -repetitions on the command line means full suite cycles.
Determinism and isolation
- Use
-gen-subscriberfor runs that don't depend on real auth, to avoid needing a shared credential file. - Pin the templates directory; a stale catalog can mask a flow rename. See the flow and suite catalog.
- Each job is an isolated process — no shared local DB to clean between jobs.
- Set
D3X_LICENSE_FILE(or install~/.dflux/runner.lic//etc/dflux/runner.lic/ the matchingd3x.liccompound file) so licensed entrypoints start cleanly.
Troubleshooting
Flaky timeouts on small cores — the AMF takes longer to respond under cold load. Raise -timeout 60s on the first run of the day, or warm the core with a check invocation first.
The exit code is 1 but the run "should" have passed — read the printed verdict and the json/junit report. all_passed only means no check failed; UEs that never reached a terminal state, a failed final_checks assertion, or a failed post-run latency threshold all verdict-fail. Add -trace for TX/RX hex dumps.
Exit code 3 — verdict unsupported. The core answered that it does not implement the scenario. Treat it as skip in a permissive gate; do not rewrite it to 0 unless that is the policy you want.
The captured log is empty — the run hard-failed before printing. Redirect stderr (2>&1) into the same file so the error is captured alongside the summary.
Exit code 2 — bad flags (missing -templates / -c, unknown -output). Fix the invocation rather than treating it as a test failure.
Where to go next
- Running flows and suites — full flag surface and report capture.
- Agent mode — continuous lab workers attached to the control plane.
- CLI reference — every flag on
run-flowandrun-suite. - Flow schema — encode latency and path assertions as post-run checks.