JSON output, CI-friendly modes, and stable integration contracts.
LATdx fits existing tooling. The output streams follow a strict contract, JSON modes are stable, and the live renderer disables itself when piped or run under CI.
Stream Contract
The CLI separates payload from diagnostics on the two output streams:
| Stream | What’s on it |
|---|---|
| stdout | Command result only. With --json, exactly one JSON document. Without --json, the human-formatted result. |
| stderr | Logs (text or NDJSON), spinners, the live test renderer, --progress-events, and interactive prompts. |
The two streams are never mixed. Pipes like latdx test run --json | jq work without any extra flags.
The CLI automatically turns off its interactive terminal output (spinner, live renderer) when:
- stdout is not a TTY,
CIis set to any non-empty value,TERM=dumb,- or
--jsonis passed.
CI text output still uses ANSI color for chips, result glyphs, and progress accents when color is supported. You can force the degraded no-ANSI mode with --plain, --no-color, or NO_COLOR. There is no --live / --no-live toggle; the live renderer is automatic.
The --json envelope on stdout is stable and drop-in compatible with sf apex test run --json. The NDJSON event stream from --progress-events is also stable. result.coverage inside the JSON envelope is not populated.
Output Modes
Pretty (default)
The default mode renders a live view: spinner, method-by-method progress, and a color summary on completion. It auto-disables in the non-interactive cases listed under Stream Contract above (non-TTY stdout, CI, TERM=dumb, --json). In plain mode the ten-second stderr heartbeat carries aggregate tests / suites progress in an interactive terminal; under CI / non-TTY that aggregate snapshot is hidden by default (the heartbeat shows a minimal liveness line) unless you pass --progress. Per-method result rows wait until the run has completed; CI keeps ANSI color for the line-buffered text unless color is explicitly disabled.
latdx test run -o my-orgUse --plain to force the degraded mode in an interactive shell.
CI Recipes
latdx test run is non-interactive when the CI env var is set (any value except empty or an opt-out: false, 0, off, no) or stdout is not a TTY: it never blocks on a confirmation prompt. The Delete all logs to continue? debug-log-storage prompt auto-accepts and retries. Set LATDX_AUTO_CONFIRM=true to also force auto-confirmation in interactive shells.
GitHub Actions
name: Apex Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
env:
CI: "true"
NO_COLOR: "1"
SF_AUTH_URL: ${{ secrets.SF_AUTH_URL }}
steps:
- uses: actions/checkout@v4
- name: Install Salesforce CLI
run: npm install --global @salesforce/cli
- name: Authenticate org
run: |
echo "$SF_AUTH_URL" | sf org login sfdx-url --sfdx-url-stdin --alias ci-org --set-default
- name: Install LATdx
run: curl -fsSL https://latdx.com/install.sh | bash
- name: Verify org readiness
run: latdx --version -o ci-org
- name: Run tests
run: latdx test run --json -o ci-org > test-result.json
- name: Upload result
if: always()
uses: actions/upload-artifact@v4
with:
name: latdx-test-result
path: test-result.json--json > file.json is the simplest way to capture results: stdout is a clean JSON document, while logs and progress remain on stderr (and in the Actions log).
Generic Shell
export CI=true
latdx test run --json -o ci-org > test-result.json
exit_code=$?
jq '.result.summary | {outcome, passing, failing}' test-result.json
exit $exit_code--progress-events pairs naturally with custom dashboards or chat notifications:
latdx test run --json --progress-events -o ci-org > result.json 2> events.ndjson
# tail -f events.ndjson | jq -c 'select(.event == "complete")'Selective Runs With Another Tool
latdx test run --dry-run resolves the exact selection a run would execute (including --affected reach analysis and workspace discovery) and prints it without running anything, so LATdx can act as the test-selection engine for any other runner. Stdout carries only the selection, one entry per line; see Dry run for the full output contract.
Run only the tests affected by the current git delta through the Salesforce CLI:
latdx test run --affected --dry-run | xargs sf apex run test --synchronous --testsAn empty selection prints nothing and exits 0; xargs then skips the sf call entirely (pass -r to GNU xargs on Linux for the same behavior).
Validate a deployment with only the affected test classes (RunSpecifiedTests requires at least one test, so guard the empty case):
tests=$(latdx test run --affected --dry-run --classes-only)
if [ -n "$tests" ]; then
sf project deploy validate --source-dir force-app --test-level RunSpecifiedTests --tests $tests
else
echo "No affected tests; skipping test-level validation"
fiFor scripted consumers, --dry-run --json emits one document with tests, classes, and a summary, and --delimiter , joins the plain output into a single shell token.
Container Image
LATdx ships static binaries with no Node runtime. A minimal container only needs the binary plus sf and a CA bundle:
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl bash git && rm -rf /var/lib/apt/lists/*
RUN npm install --global @salesforce/cli
RUN curl -fsSL https://latdx.com/install.sh | bash
ENV CI=true NO_COLOR=1
ENTRYPOINT ["latdx"]Exit Codes
| Code | Meaning |
|---|---|
0 | Successful run / list / adapt path; all tests passed. |
1 | Validation failure, runtime error, or one-or-more test failures. |
latdx uninstall and latdx restore also exit 1 when class restoration fails. latdx --version -o <alias> exits 1 if the org cannot be reached. Prefer --json in CI: parse the envelope to distinguish “test failed” (status: 1, result.summary.failing > 0) from “engine failed” (no envelope, non-zero exit, log line on stderr).