GitLab CI integration · REST API

GitLab CI Integration for API Testing

Shift-Left API connects to GitLab CI through its public REST API today: a job signs in, triggers a test pack, waits for the new run and fails the pipeline when the pass rate misses your threshold. A packaged CI/CD component is public in source but not yet in the GitLab catalog. Works with self-managed GitLab.

The Shift-Left API CI/CD component is not in the GitLab CI/CD Catalog yet, and the totalshiftleft/ci-runner image it uses is not on Docker Hub, so an include: component: reference will not resolve today. Its source is public — see gitlab-component/. Everything below is a real, working pattern using the public REST API directly.

What this integration gives you

The job below runs a Shift-Left API test pack as part of any GitLab CI pipeline, using nothing but curl and jq against the public REST API: authenticate, trigger a run, and check the result. Use it to gate merge requests on API quality, run scheduled regression suites against staging environments, or chain API tests into broader release pipelines.

  • Quality gates on merge requests

    Exit non-zero when the pass rate misses your threshold — blocks the merge via GitLab's "Pipelines must succeed" setting.

  • JSON results

    The execution endpoint returns structured JSON; pipe it into your own reporting or artifact upload step.

  • Self-managed friendly

    Works on any GitLab Runner — shared, self-managed, or air-gapped — as long as the runner can reach your Shift-Left deployment.

  • Protected variable support

    Credentials stay masked in logs and only expose on protected branches via GitLab's built-in CI/CD variable scoping.

.gitlab-ci.yml example

Drop this into your .gitlab-ci.yml. Stores credentials as protected, masked CI/CD variables, runs on every merge request and main-branch push, and fails the job when the pass rate drops below 95%.

stages:
  - test

api-tests:
  stage: test
  image: alpine:latest
  variables:
    SHIFTLEFT_URL: $SHIFTLEFT_URL
    SHIFTLEFT_EMAIL: $SHIFTLEFT_EMAIL
    SHIFTLEFT_PASSWORD: $SHIFTLEFT_PASSWORD
    SHIFTLEFT_TEST_PACK_ID: $SHIFTLEFT_TEST_PACK_ID
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      set -euo pipefail
      PACK="$SHIFTLEFT_TEST_PACK_ID"
      TOKEN=$(curl -sS -f -X POST "$SHIFTLEFT_URL/api/v1/login" \
        -H "Content-Type: application/json" \
        -d "{\"email\":\"$SHIFTLEFT_EMAIL\",\"password\":\"$SHIFTLEFT_PASSWORD\"}" \
        | jq -r .token)
      AUTH="Authorization: Bearer $TOKEN"
      STATUS_URL="$SHIFTLEFT_URL/api/v1/test-packs/$PACK/status"

      # Note the pack's latest run first, so the previous run is never graded
      BEFORE=$(curl -sS -f "$STATUS_URL" -H "$AUTH" | jq -r '.executionId // empty')
      # Start the pack (the server answers 409 if it is already running)
      curl -sS -f -X POST "$SHIFTLEFT_URL/api/v1/test-packs/$PACK/run" -H "$AUTH" > /dev/null

      # Wait up to 60 minutes for a NEW run to finish
      DONE=0
      for i in $(seq 1 360); do
        RESULT=$(curl -sS -f "$STATUS_URL" -H "$AUTH")
        RUN_ID=$(echo "$RESULT" | jq -r '.executionId // empty')
        STATE=$(echo "$RESULT" | jq -r '.status // empty' | tr '[:lower:]' '[:upper:]')
        if [ -n "$RUN_ID" ] && [ "$RUN_ID" != "$BEFORE" ]; then
          case "$STATE" in RUNNING|PENDING|QUEUED|"") ;; *) DONE=1; break ;; esac
        fi
        sleep 10
      done
      [ "$DONE" = 1 ] || { echo "Timed out waiting for a new run"; exit 1; }

      echo "$RESULT" | jq .
      # Reads executionId, status and successRate; if your installation's Swagger page
      # names them differently, adjust the jq filters. Fails closed below 95%.
      echo "$RESULT" | jq -e '(.successRate // 0) >= 95' > /dev/null
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

CI/CD variables

Configure these as GitLab CI/CD variables at the project, group, or instance level. Use the Protected and Masked flags for any value containing credentials.

VariableDescription
SHIFTLEFT_URLBase URL of your Shift-Left API deployment. Store as a CI/CD variable; mark as masked.
SHIFTLEFT_EMAIL / SHIFTLEFT_PASSWORDAuthentication credentials. Store as masked, protected CI/CD variables.
SHIFTLEFT_TEST_PACK_IDID of the test pack to execute. Project- or group-level variable.

Quality gates on merge requests

Combine the script's exit code with GitLab's merge request settings to block merges on API quality regressions:

  1. In GitLab: Settings → Merge Requests → Pipelines must succeed.
  2. Edit the pass-rate threshold inline in the jq -e check at the end of the script (default is 95).
  3. Extend the script with your own logic — error-test counts, specific endpoint checks — since you control the shell, not a fixed set of image inputs.
  4. Optional: require a manual approver from the QE / Architecture team for protected branches.

For data-flow specifics — including how the runner reaches a self-hosted Shift-Left API deployment — see the security page and deployment page.

The component, once it is published

Not yet installable

The component source is public and wraps @totalshiftleft/ci — the single implementation behind the GitHub Action, the Azure DevOps task, the CircleCI orb and the Bitbucket pipe, so it grades a run identically to all of them. It is not in the CI/CD Catalog yet, so the snippet below is what the include will look like, not something you can run today.

stages:
  - test

include:
  - component: gitlab.com/totalshiftleft/shiftleft-ci/run-test-pack@1.0.0
    inputs:
      server-url: https://tenant.totalshiftleft.ai
      pack-id: pack_123
      pass-threshold-percent: 95

The component reads credentials by variable name — api-email-variable and api-password-variable default to SHIFTLEFT_API_EMAIL and SHIFTLEFT_API_PASSWORD — so no secret ever lands in .gitlab-ci.yml. It publishes both artifacts with when: always and feeds the JUnit XML to artifacts:reports:junit, so results show up in the merge request widget and the pipeline's Tests tab.

Gate decision codes

Printed to the job log as [ShiftLeft] Output decision=<code> and repeated in the JSON summary artifact. The job exits 0 when the gate passes and 1 when it fails, so it gates the pipeline.

DecisionMeaning
PASSEDThe run finished and cleared every gate you configured.
GATE_FAIL_THRESHOLDThe run finished below your pass-rate threshold.
GATE_FAIL_ERROR_TESTSAt least one test finished in ERROR and fail-on-error-tests was on.
FAILEDThe execution itself failed.
COMPLETED_WITH_ISSUESThe gate failed but gate-failure-result was set to warn instead.
OKThe run completed with no gate configured to judge it.
TIMEOUTThe run had not finished within timeout-minutes.
TRIGGER_ONLYwait-for-completion was false, so nothing was graded.

Step-by-step setup, every input and troubleshooting: Any CI via the REST API (help centre). For the tool-neutral how-to on running API tests in GitLab CI, read API test automation with CI/CD, step by step; to run security or load tests from the same packs, see API security testing and API load testing (paid add-ons).

GitLab CI integration — FAQ

Contact us at

info@totalshiftleft.com

to learn more

  • Is there a first-party Shift-Left API GitLab CI component?

    The component exists in source — it lives in the public Shift-Left-API-Integrations repository and wraps the same shared runner as every other Shift-Left API CI integration. It is not in the GitLab CI/CD Catalog yet, and the container image it runs is not on Docker Hub, so an include: component: reference will not resolve today. Until it does, the script above — calling the public REST API directly with curl and jq from any GitLab Runner — is the real, working way to run test packs from GitLab CI.

  • Does this work with self-managed GitLab and self-hosted Shift-Left API?

    Yes. The script runs on any GitLab Runner — shared or self-managed — and calls the public /api/v1 endpoints, so any reachable Shift-Left deployment works. For self-hosted Shift-Left deployments behind a firewall, use a GitLab Runner inside the same network.

  • How do I gate merge requests on API test results?

    The example script exits non-zero when the pass rate drops below your threshold, which fails the job. Combine with GitLab's "Pipelines must succeed" merge request setting under Settings → Merge Requests to block merges on API test failures.

  • Can I run multiple test packs in parallel?

    Yes. Use a parallel:matrix block with different SHIFTLEFT_TEST_PACK_ID values, or define multiple jobs in the test stage. Each job is independent and runs on its own runner.

  • How do credentials reach the runner safely?

    Use protected, masked CI/CD variables defined at the project, group, or instance level. Protected variables are exposed only on protected branches and tags. Masked variables are scrubbed from job logs. Never inline credentials in .gitlab-ci.yml.

  • When will the component be installable?

    The source is written, tested and public. Publishing it means mirroring the directory into a GitLab-hosted catalog project and tagging a release, plus pushing the runner image to Docker Hub. We are not committing to a date. This REST API pattern will keep working either way — the component is a convenience wrapper around the same endpoints, not a replacement for them.

Add API testing to your GitLab pipelines

Start a 15-day trial, or use Professional or Enterprise. The REST API pattern above works today — no need to wait for the component.

Updated