CI/CD Integration - GitHub Actions, GitLab, Jenkins, CircleCI
The Bitbucket CLI automates pull request (PR) workflows, reports build statuses back to commits, and triggers Bitbucket Pipelines from any CI system.
Quick setup
Section titled “Quick setup”-
Store credentials as secrets in your CI/CD platform.
BB_USERNAME— your Bitbucket usernameBB_API_TOKEN— your Bitbucket API token
-
Install Bun and the CLI. The CLI is Bun-only; it exits immediately under Node.js.
Terminal window curl -fsSL https://bun.sh/install | bashexport PATH="$HOME/.bun/bin:$PATH"bun install -g @pilatos/bitbucket-cliOn an image that already ships Bun (
oven/bun:latest) skip thecurlline and keep the other two — those images contain no Node.js and no npm. -
Authenticate. With
BB_API_TOKENset in the environment,bb auth logintakes the API-token path and stores the credentials. With no token set it opens a browser for OAuth and waits five minutes for a callback that never arrives in a headless runner.Terminal window bb auth login -
Run commands with explicit workspace and repository flags.
Terminal window bb pr list -w myworkspace -r myrepo --json
Paging
Section titled “Paging”List commands return 25 items by default. Pass --limit <n> to raise or lower that cap, or --all to fetch every page — --all overrides --limit.
bb pr list -w myworkspace -r myrepo --all --jsonSee Global Flags.
Reading JSON without an external jq
Section titled “Reading JSON without an external jq”--json [fields] and --jq <expression> are global flags. --jq runs an embedded jq, so it works on minimal CI images and on Windows agents that have no jq binary. --jq requires --json.
# Project to a comma-separated field listbb pr list -w myworkspace -r myrepo --json id,title
# Filter with the embedded jqbb pr list -w myworkspace -r myrepo --all --json --jq '.pullRequests[].id'Projection runs before jq, and --json <fields> drops the envelope on list commands — count disappears once you project. See Scripting & Automation.
Platform examples
Section titled “Platform examples”Each snippet shows the platform-specific part: how secrets reach the job, and how ~/.bun/bin gets on PATH. The install and bb auth login steps are the same everywhere.
GitHub Actions
Section titled “GitHub Actions”name: Bitbucket PR Check
on: push: branches: [main] pull_request:
jobs: check-bitbucket-prs: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v4
- name: Setup Bun uses: oven-sh/setup-bun@v1 with: bun-version: latest
- name: Install Bitbucket CLI run: bun install -g @pilatos/bitbucket-cli
- name: List open PRs env: BB_USERNAME: ${{ secrets.BB_USERNAME }} BB_API_TOKEN: ${{ secrets.BB_API_TOKEN }} run: | bb auth login bb pr list -w myworkspace -r myrepo --json --jq '.count'oven-sh/setup-bun puts bun on PATH for you, including globally installed binaries.
GitLab CI
Section titled “GitLab CI”stages: - check
variables: BB_WORKSPACE: myworkspace BB_REPO: myrepo
check-prs: stage: check image: oven/bun:latest
before_script: - bun install -g @pilatos/bitbucket-cli - export PATH="$HOME/.bun/bin:$PATH" - bb auth login
script: - bb pr list -w $BB_WORKSPACE -r $BB_REPO --json - bb repo view -w $BB_WORKSPACE -r $BB_REPODefine BB_USERNAME and BB_API_TOKEN under Settings > CI/CD > Variables (masked, protected). GitLab exports project variables into the job environment, so no variables: entry is needed for them.
Bitbucket Pipelines
Section titled “Bitbucket Pipelines”image: oven/bun:latest
pipelines: default: - step: name: Check PRs script: - bun install -g @pilatos/bitbucket-cli - export PATH="$HOME/.bun/bin:$PATH" - bb auth login - bb pr list -w $BITBUCKET_WORKSPACE -r $BITBUCKET_REPO_SLUG --json
pull-requests: '**': - step: name: PR Info script: - bun install -g @pilatos/bitbucket-cli - export PATH="$HOME/.bun/bin:$PATH" - bb auth login - bb pr view $BITBUCKET_PR_ID -w $BITBUCKET_WORKSPACE -r $BITBUCKET_REPO_SLUG
definitions: caches: bun: ~/.bunPipelines injects BITBUCKET_WORKSPACE, BITBUCKET_REPO_SLUG, and BITBUCKET_PR_ID, which the snippet above passes to -w, -r, and bb pr view.
Jenkins
Section titled “Jenkins”pipeline { agent any
environment { BB_USERNAME = credentials('bitbucket-username') BB_API_TOKEN = credentials('bitbucket-token') }
stages { stage('Setup') { steps { sh 'curl -fsSL https://bun.sh/install | bash' sh 'export PATH="$HOME/.bun/bin:$PATH" && bun install -g @pilatos/bitbucket-cli' sh 'export PATH="$HOME/.bun/bin:$PATH" && bb auth login' } }
stage('Check PRs') { steps { sh ''' export PATH="$HOME/.bun/bin:$PATH" bb pr list -w myworkspace -r myrepo --json --jq '.count' ''' } } }}Each sh step is a fresh shell, so PATH has to be exported in every one.
To merge approved PRs from Jenkins, run the script in Merge approved PRs automatically as a build step.
Azure DevOps
Section titled “Azure DevOps”trigger: - main
pool: vmImage: 'ubuntu-latest'
variables: - group: bitbucket-credentials # Variable group with BB_USERNAME, BB_API_TOKEN
steps: - script: | curl -fsSL https://bun.sh/install | bash echo "##vso[task.prependpath]$HOME/.bun/bin" displayName: 'Install Bun'
- script: bun install -g @pilatos/bitbucket-cli displayName: 'Install Bitbucket CLI'
- script: | bb auth login bb pr list -w myworkspace -r myrepo --json displayName: 'List PRs' env: BB_USERNAME: $(BB_USERNAME) BB_API_TOKEN: $(BB_API_TOKEN)##vso[task.prependpath] puts ~/.bun/bin on PATH for every later step.
Common use cases
Section titled “Common use cases”Report build status back to Bitbucket
Section titled “Report build status back to Bitbucket”bb status set <sha> creates or updates a build status on a commit. It is idempotent per --key, so calling it again with the same key replaces the previous status instead of adding a second one.
bb status set "$GITHUB_SHA" -w myworkspace -r myrepo \ --key CI --state INPROGRESS --url "$RUN_URL"
# ... run the build ...
bb status set "$GITHUB_SHA" -w myworkspace -r myrepo \ --key CI --state SUCCESSFUL --url "$RUN_URL" --description "All tests passed"--state accepts INPROGRESS, SUCCESSFUL, FAILED, or STOPPED. --name, --description, and --refname are optional. Read statuses back with bb status list <sha>.
Trigger a Bitbucket pipeline from another CI system
Section titled “Trigger a Bitbucket pipeline from another CI system”# Latest commit on a branchbb pipeline run -w myworkspace -r myrepo --branch main
# A custom pipeline from bitbucket-pipelines.yml, with variablesbb pipeline run -w myworkspace -r myrepo \ --pipeline deploy-prod --var ENV=prod --var DRY_RUN=falsebb pipeline list --status FAILED, bb pipeline view <id>, bb pipeline logs <id> --step <n>, and bb pipeline stop <id> cover the rest. See bb pipeline.
Create a PR from a branch
Section titled “Create a PR from a branch”Create the PR only if one does not already exist for the source branch. This works the same whether the branch came from a feature push or a scheduled dependency bump.
BRANCH=${GITHUB_REF#refs/heads/}
EXISTING=$(bb pr list -w myworkspace -r myrepo --all --json \ --jq "[.pullRequests[] | select(.source.branch.name == \"$BRANCH\")] | length")
if [ "$EXISTING" -eq 0 ]; then bb pr create -w myworkspace -r myrepo \ -t "chore: $BRANCH" \ -b "Opened automatically by CI" \ -s "$BRANCH" \ -d mainfiMerge approved PRs automatically
Section titled “Merge approved PRs automatically”This script merges as soon as any reviewer has approved. To wait for all CI checks to pass first — the more common production pattern — see Auto-merge on green CI.
#!/bin/bash# auto-merge.sh - Run in CI on schedule
set -e
WORKSPACE="myworkspace"REPO="myrepo"
# --all is required: without it only the first 25 open PRs are consideredopen_prs=$(bb pr list -w "$WORKSPACE" -r "$REPO" --all --json --jq '.pullRequests[].id')
for pr_id in $open_prs; do is_approved=$(bb pr view "$pr_id" -w "$WORKSPACE" -r "$REPO" --json \ --jq '[.participants[] | select(.approved == true)] | length > 0')
if [ "$is_approved" != "true" ]; then sleep 1 continue fi
if bb pr merge "$pr_id" -w "$WORKSPACE" -r "$REPO" --strategy squash --close-source-branch; then echo "Merged PR #$pr_id" else echo "Could not merge PR #$pr_id (conflicts or failing checks)" fi
sleep 2doneGenerate a changelog from merged PRs
Section titled “Generate a changelog from merged PRs”#!/bin/bashWORKSPACE="myworkspace"REPO="myrepo"SINCE_DATE=$(date -d '7 days ago' +%Y-%m-%d 2>/dev/null || \ date -v-7d +%Y-%m-%d)
echo "# Changelog - Week of $(date +%Y-%m-%d)"echo ""
# External jq here because the program needs --arg; --jq takes no variablesbb pr list -w "$WORKSPACE" -r "$REPO" -s MERGED --all --json | jq -r --arg since "$SINCE_DATE" ' .pullRequests[] | select(.updated_on >= $since) | "- \(.title) (#\(.id)) by @\(.author.nickname // .author.display_name // "unknown")"'Exit codes and errors
Section titled “Exit codes and errors”Every command exits 0 on success and 1 on any failure. There is no per-error exit code — the specific ErrorCode lives in the error payload.
Under --json, the error envelope is written to stderr as one compact line while stdout stays byte-clean JSON:
{"name":"BBError","code":1001,"message":"Authentication required. Run 'bb auth login' or set BB_USERNAME and BB_API_TOKEN."}401, 403, and 404 failures carry an extra hint string. See Scripting & Automation and Error Codes.
Security considerations
Section titled “Security considerations”Token scopes
Section titled “Token scopes”Create a dedicated CI/CD token with minimal permissions:
| Use case | Required scopes |
|---|---|
| Verify identity | read:user:bitbucket |
| Read-only checks | + read:repository:bitbucket, read:pullrequest:bitbucket |
| Create/merge PRs | + write:pullrequest:bitbucket |
| Report build statuses, write repository data | + write:repository:bitbucket |
| Create repositories | + admin:repository:bitbucket |
| Delete repositories | + delete:repository:bitbucket |
The + notation is cumulative for the read and write scopes only. admin:repository:bitbucket and delete:repository:bitbucket are separate scopes, not supersets of each other or of write:repository:bitbucket — grant each one you actually need. Full list: Token Scopes.
Secrets management
Section titled “Secrets management”Store in Settings > Secrets and variables > Actions
Store in Settings > CI/CD > Variables (masked, protected)
Use Credentials plugin with Secret text type
Store as secured repository or workspace variables
Troubleshooting CI/CD
Section titled “Troubleshooting CI/CD”Authentication fails
Section titled “Authentication fails”✗ Authentication required. Run 'bb auth login' or set BB_USERNAME and BB_API_TOKEN.Under --json the same failure appears on stderr as {"name":"BBError","code":1001,...}.
Check that both variables reach the job, and that the token has not expired:
echo "Username set: $([ -n "$BB_USERNAME" ] && echo 'yes' || echo 'no')"echo "Token set: $([ -n "$BB_API_TOKEN" ] && echo 'yes' || echo 'no')"The auth step hangs
Section titled “The auth step hangs”bb auth login only uses the API-token path when BB_API_TOKEN is set (or when you pass -u/-p/--with-token). Otherwise it starts the OAuth browser flow on http://localhost:19872/callback and blocks for five minutes. Set BB_API_TOKEN in the step’s environment.
Rate limiting in loops
Section titled “Rate limiting in loops”Add a delay between API calls:
for pr_id in $pr_ids; do bb pr view "$pr_id" -w myworkspace -r myrepo --json sleep 2donebb: command not found
Section titled “bb: command not found”bun install -g puts binaries in ~/.bun/bin, which is not on PATH in a fresh shell. Export it before calling bb, in the same shell invocation:
export PATH="$HOME/.bun/bin:$PATH"bb --version