Appearance
suluctl CLI
suluctl is Sulu's open-source (Apache-2.0), zero-dependency CLI that streams test reports into the import API. Single static binary — nothing to add to your test project.
Install
bash
curl -fsSL https://raw.githubusercontent.com/ellyZz/suluctl/main/install.sh | shOr grab a binary from GitHub Releases, or use the container image ghcr.io/ellyzz/suluctl in CI.
Configure
| Env var | Required | Meaning |
|---|---|---|
SULU_URL | yes | Your Sulu base URL |
SULU_TOKEN | yes | API token (Profile → API keys) |
SULU_PROJECT_ID | yes | Numeric destination project id |
SULU_LAUNCH_NAME | no | Launch display name |
SULU_LAUNCH_UUID | no | Upload into an existing launch (set automatically by Sulu Jobs — see below) |
Flags override env: --url --token --project --launch-name --launch-uuid --env --tag --env-var --insecure.
One-shot upload
bash
suluctl upload --results ./allure-results
# or a JUnit XML directory:
suluctl upload --results ./build/test-results/testCreates a launch, uploads everything in batches, finishes the launch, and prints a per-file summary plus the launch link. Exit code is non-zero only on total failure (config or auth errors, plan limits, exhausted retries, unreachable server) — individual bad files are reported per-file and don't fail your build.
Live streaming (watch mode)
bash
suluctl watch --results ./allure-results -- pytest --alluredir=allure-resultsRuns your test command, streams result files into Sulu as they appear (results show up in the launch while tests are still running), and always exits with your test command's exit code — safe to wrap in any CI step. If Sulu is unreachable, watch degrades to a transparent wrapper: your tests still run and the exit code still passes through.
Triggered from a Sulu Job
When a run is dispatched by a Sulu Job (/app/jobs), Sulu pre-creates the launch and forwards its id to your workflow as the SULU_LAUNCH_UUID environment variable. suluctl reads it automatically and uploads into that pre-created launch instead of creating a new one — no extra flags or configuration needed (it works for both upload and watch).
A few things to know:
- The Job must target the same project as
SULU_PROJECT_ID. If they differ, Sulu rejects the upload with a403("clientUuid belongs to a launch in another project"). - The launch keeps the name from the Job, so
--launch-name/SULU_LAUNCH_NAMEhas no effect on a Job-triggered run. - For the cleanest results, dispatch the Job without a specific test-case or test-plan selection so the launch is populated entirely from your uploaded report.