Skip to content

CI integration

Trigger your test workflows from Sulu and have the results land in the same Launch you created — no manual wiring on the test framework side.

What you get

  • Launch a configured GitHub Actions or GitLab CI workflow from inside Sulu.
  • Test results pushed by the Sulu SDK land in the same Sulu Launch that Sulu pre-created (no duplicate launches).
  • See live status from the Sulu Launches page; click through to the GH / GL run UI for full logs.

Required versions

  • Sulu: v1.9.0 or later
  • sulu-java-common SDK: 0.2.0 or later

This is the SDK version that reads SULU_LAUNCH_UUID from the env and binds your test run to the pre-created Sulu Launch. Without it, results go to a fresh launch and you'll see two launches per CI run.

Set up

1. Create a Personal Access Token

GitHub — Settings → Developer settings → Personal access tokens.

  • Classic: scopes workflow (+ repo if private)
  • Fine-grained: Actions: Read+Write, Contents: Read

GitLab — User Settings → Access Tokens.

  • Scopes: api, read_repository

2. Add a CI connection in Sulu

Project Settings → Integrations → CI Connections → + New connection.

Paste the PAT and click Test connection before saving. Sulu queries /user to validate scopes; you'll see your username on success.

3. Prepare your workflow

Your workflow file must accept workflow_dispatch with Sulu's reserved inputs. Sulu's JobDialog has a built-in validator that fetches your YAML and tells you exactly what's missing, with a copy-paste snippet.

Minimum block:

yaml
on:
  workflow_dispatch:
    inputs:
      SULU_LAUNCH_ID:    { required: false, type: string }
      SULU_LAUNCH_UUID:  { required: false, type: string }
      SULU_JOB_RUN_ID:   { required: false, type: string }
      SULU_API_URL:      { required: false, type: string }
      SULU_ENV_NAME:     { required: false, type: string }
      SULU_TEST_FILTER:  { required: false, type: string }

# Recommended: surface SULU_JOB_RUN_ID in the run name so Sulu can match runs accurately
run-name: 'tests / SULU_JOB_RUN_ID=${{ inputs.SULU_JOB_RUN_ID }}'

jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: 21 }
      - name: Run tests
        env:
          # Pass the inputs into the SDK's env
          SULU_LAUNCH_ID:   ${{ inputs.SULU_LAUNCH_ID }}
          SULU_LAUNCH_UUID: ${{ inputs.SULU_LAUNCH_UUID }}
        run: ./gradlew test

4. Create a job in Sulu

Jobs page → + New job. Pick your CI connection, fill repo (e.g. owner/repo), pick the workflow from the dropdown, set git ref.

5. Run

Jobs page → click the Play button. Sulu pre-creates a Launch, dispatches your workflow, and you'll see results stream in as your tests run.

You can also run a subset of test cases from the Test Cases page: select cases → Run → Auto mode → pick the job.

Token scopes — full table

ProviderToken typeRequired scopes
GitHubClassic PATworkflow + repo (if private)
GitHubFine-grained PATActions: Read+Write, Contents: Read
GitLabPersonal Access Tokenapi, read_repository

From your test framework

The Sulu SDK reads SULU_LAUNCH_UUID from the env and uses it as the clientUuid for the launch — Sulu's V2 ingest is idempotent on clientUuid, so test results land in the pre-created Launch instead of a fresh one.

Minimum sulu.properties in your test repo:

properties
sulu.url=https://your-sulu.example
sulu.token=sulu_***                  # USER-scoped API token
sulu.project.id=42
sulu.launch.name=Smoke               # Used only when not triggered from Sulu
sulu.async=true                      # Recommended (V2 path)

You do not need to set sulu.launch.uuid in sulu.properties — the SDK auto-detects SULU_LAUNCH_UUID from the env when triggered from Sulu, and falls back to a randomly-generated UUID for local runs.

Uploading reports with suluctl

If you stream reports with suluctl (the import path) instead of an SDK, it works the same way: suluctl (upload and watch) auto-detects SULU_LAUNCH_UUID and sends it as the import clientUuid, so a Job-triggered run lands in the pre-created Launch. No flags needed. The Job must target the same project as SULU_PROJECT_ID, and the launch keeps the Job's name. See the suluctl page for details.

Troubleshooting

SymptomLikely cause
Test connection returns 401 Bad credentialsPAT expired or wrong token type
Test connection returns 200 but Run returns 404Repository path wrong (e.g. typo in owner/repo)
Test connection passes, workflow picker loads, but Run fails with 403 Resource not accessible by personal access tokenPAT has read scopes but lacks workflow (classic) / Actions: Read and write (fine-grained). Reading workflows only needs repo / Actions:Read; dispatching needs the write scope. Regenerate the token with the right scope and re-paste it.
Run returns 422 Validation failedWorkflow missing workflow_dispatch: — fix per validator snippet in JobDialog
Run starts but results don't appear in Sulu LaunchSDK version too old (doesn't read SULU_LAUNCH_UUID) — bump sulu-java-common to ≥ 0.2.0
Two Launches per run (one zombie + one real)Same as above — SDK didn't pick up SULU_LAUNCH_UUID; results went to a fresh launch

Sulu Test Management System