Bitbucket Pipelines integration
API testing in Bitbucket Pipelines
The first-party Bitbucket pipe is public in source and awaiting image publication. Total Shift Left's public REST API lets you trigger test packs, gate merges on pass-rate thresholds, and pull results into any pipeline today.
The Total Shift Left Bitbucket pipe is not on Docker Hub yet, so a pipe: totalshiftleft/shiftleft-test-pack reference will not resolve today. Its source is public — see bitbucket-pipe/. Everything below is a real, working pattern using the public REST API directly.
What this integration gives you
The step below runs a Total Shift Left test pack as part of any Bitbucket Pipelines pipeline, using nothing but curl and jq against the public REST API: authenticate, trigger a run, and check the result. Use it to gate pull requests on API quality, run scheduled regression suites, or chain into broader release pipelines on Bitbucket Cloud.
Quality gates on pull requests
Exit non-zero when the pass rate misses your threshold, integrating cleanly with "Require successful builds" merge checks.
JSON results
The execution endpoint returns structured JSON; pipe it into your own reporting or artifact upload step.
Workspace variable scoping
Share credentials across repos via Workspace variables; per-repo overrides via Repository variables.
Self-hosted runner support
Run on Bitbucket cloud runners or your own self-hosted runners for air-gapped Shift-Left deployments.
bitbucket-pipelines.yml example
Drop this into your bitbucket-pipelines.yml. Runs on every pull request and on main-branch merges, and fails the step when the pass rate drops below 95%.
image: atlassian/default-image:4
pipelines:
pull-requests:
'**':
- step:
name: Total Shift Left Tests
script:
- apt-get update -qq && apt-get install -y -qq jq
- TOKEN=$(curl -s -X POST "$SHIFTLEFT_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$SHIFTLEFT_EMAIL\",\"password\":\"$SHIFTLEFT_PASSWORD\"}" \
| jq -r .token)
- EXEC_ID=$(curl -s -X POST "$SHIFTLEFT_URL/api/v1/test-packs/$SHIFTLEFT_TEST_PACK_ID/run" \
-H "Authorization: Bearer $TOKEN" | jq -r .executionId)
- |
# Poll until the run has a result, up to 2 minutes
for i in $(seq 1 24); do
RESULT=$(curl -s "$SHIFTLEFT_URL/api/v1/executions/$EXEC_ID" -H "Authorization: Bearer $TOKEN")
[ "$(echo "$RESULT" | jq -r '.passRate != null')" = "true" ] && break
sleep 5
done
echo "$RESULT" | jq .
# Fails closed: a missing/null/non-numeric passRate (auth error, bad execution
# id, run still in progress after the timeout above) is a failure, not a pass.
echo "$RESULT" | jq -e '(.passRate // 0) >= 95' > /dev/null
branches:
main:
- step:
name: Total Shift Left Tests (main)
script:
- apt-get update -qq && apt-get install -y -qq jq
- TOKEN=$(curl -s -X POST "$SHIFTLEFT_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$SHIFTLEFT_EMAIL\",\"password\":\"$SHIFTLEFT_PASSWORD\"}" \
| jq -r .token)
- EXEC_ID=$(curl -s -X POST "$SHIFTLEFT_URL/api/v1/test-packs/$SHIFTLEFT_TEST_PACK_ID/run" \
-H "Authorization: Bearer $TOKEN" | jq -r .executionId)
- |
# Poll until the run has a result, up to 2 minutes
for i in $(seq 1 24); do
RESULT=$(curl -s "$SHIFTLEFT_URL/api/v1/executions/$EXEC_ID" -H "Authorization: Bearer $TOKEN")
[ "$(echo "$RESULT" | jq -r '.passRate != null')" = "true" ] && break
sleep 5
done
echo "$RESULT" | jq .
# Fails closed: a missing/null/non-numeric passRate (auth error, bad execution
# id, run still in progress after the timeout above) is a failure, not a pass.
echo "$RESULT" | jq -e '(.passRate // 0) >= 95' > /dev/nullPipeline variables
Define these in Bitbucket Repository settings → Pipelines → Repository variables (per repo) or Workspace settings → Pipelines → Workspace variables (across many repos). Mark credential values as Secured to mask them in build logs.
| Variable | Description |
|---|---|
| SHIFTLEFT_URL | Base URL of your Total Shift Left deployment. Define as a Repository, Deployment, or Workspace variable depending on scope. |
| SHIFTLEFT_EMAIL / SHIFTLEFT_PASSWORD | Authentication credentials. Always mark as Secured so values are masked in logs. |
| SHIFTLEFT_TEST_PACK_ID | ID of the test pack. Repository-scoped variable typically. |
The pipe, once it is published
Not yet installableThe pipe source is public and wraps @totalshiftleft/ci — the single implementation behind the GitHub Action, the Azure DevOps task, the CircleCI orb and the GitLab component, so it grades a run identically to all of them. The image is not on Docker Hub yet, so the snippet below is what the step will look like, not something you can run today.
pipelines:
default:
- step:
name: API tests
script:
- pipe: totalshiftleft/shiftleft-test-pack:1.0.0
variables:
SHIFTLEFT_SERVER_URL: 'https://tenant.totalshiftleft.ai'
SHIFTLEFT_API_EMAIL: $SHIFTLEFT_API_EMAIL
SHIFTLEFT_API_PASSWORD: $SHIFTLEFT_API_PASSWORD
SHIFTLEFT_PACK_ID: 'pack_123'
SHIFTLEFT_PASS_THRESHOLD_PERCENT: '95'
artifacts:
- shiftleft-test-pack-summary.json
- shiftleft-test-pack-results.xmlDefine SHIFTLEFT_API_EMAIL and SHIFTLEFT_API_PASSWORD as secured repository, deployment or workspace variables so they are masked in the log. Bitbucket picks JUnit XML up automatically from a few well-known locations, so setting SHIFTLEFT_TEST_RESULTS_XML_PATH to test-results/shiftleft.xml puts the run in the Tests tab.
Gate decision codes
Printed to the step log as [ShiftLeft] Output decision=<code> and repeated in the JSON summary artifact. The step exits 0 when the gate passes and 1 when it fails, so it gates the build.
| Decision | Meaning |
|---|---|
| PASSED | The run finished and cleared every gate you configured. |
| GATE_FAIL_THRESHOLD | The run finished below your pass-rate threshold. |
| GATE_FAIL_ERROR_TESTS | At least one test finished in ERROR and fail-on-error-tests was on. |
| FAILED | The execution itself failed. |
| COMPLETED_WITH_ISSUES | The gate failed but gate-failure-result was set to warn instead. |
| OK | The run completed with no gate configured to judge it. |
| TIMEOUT | The run had not finished within timeout-minutes. |
| TRIGGER_ONLY | wait-for-completion was false, so nothing was graded. |
Bitbucket Pipelines integration — FAQ
Is there a first-party Total Shift Left Bitbucket pipe?
The pipe exists in source — it lives in the public Shift-Left-API-Integrations repository and wraps the same shared runner as every other Total Shift Left CI integration. Neither the pipe image nor the base runner image is on Docker Hub yet, so a pipe: totalshiftleft/shiftleft-test-pack reference will not resolve today. Until it does, the script above — calling the public REST API directly with curl and jq — is the real, working way to run test packs from Bitbucket Pipelines.Does this work with Bitbucket Data Center / self-managed Bitbucket?
The script above is written for Bitbucket Cloud Pipelines. For Bitbucket Data Center / self-managed deployments, the equivalent CI is Bamboo or third-party runners — the same public REST API pattern still applies, just wired into whatever job runner you use.How do I store credentials in Bitbucket?
Use Bitbucket repository, deployment, or workspace variables with the Secured flag enabled. Secured variables are masked in logs and not exposed to forked pull requests by default. For credentials shared across many repos, use Workspace variables.How do I gate pull requests on test results?
The example script exits non-zero when the pass rate drops below your threshold. Combine with Bitbucket's "Require successful builds" merge check (Repository settings → Branch permissions → Merge checks) to block merging on API test failures.How do I run multiple test packs in parallel?
Use parallel steps inside a Bitbucket Pipelines stage. Each parallel step is independent and runs on its own runner. Note that each parallel step consumes a build minute — Bitbucket Pipelines bills by build-minutes, so heavy parallelism trades cost for wall-clock time.Can this run on a self-hosted runner?
Yes. Bitbucket Pipelines supports self-hosted runners. Configure your runner inside the same network as your self-hosted Total Shift Left deployment, then reference the runner in the runs-on key of your pipeline step.When will the pipe be installable?
The source is written, tested and public. Publishing it means pushing the shared runner image to Docker Hub and then the pipe image on top of it; a Bitbucket pipes-catalog listing is a separate manual submission to Atlassian, though the pipe works from Docker Hub without one. We are not committing to a date. This REST API pattern will keep working either way — the pipe is a convenience wrapper around the same endpoints, not a replacement for them.
More CI/CD integrations
Add API testing to your Bitbucket pipelines
Forever-free Citizen Developer or 15-day Enterprise trial. The REST API pattern above works today — no need to wait for the pipe.