Scripting & Automation - Bitbucket CLI for CI/CD Workflows
The Bitbucket CLI is designed for both interactive use and automation. This guide covers JSON output, exit codes, and scripting patterns.
JSON Output Mode
Section titled “JSON Output Mode”All commands support the --json flag for machine-readable output:
bb pr list --jsonbb repo list --jsonbb pr view 42 --jsonFor list-style commands, results are limited by default. Pass --limit when
your automation needs more rows.
Parsing with jq
Section titled “Parsing with jq”jq is the standard tool for processing JSON:
# List all PR titlesbb pr list --json | jq '.pullRequests[].title'
# Get PR countbb pr list --json | jq '.count'
# Filter by statebb pr list -s MERGED --json | jq '.count'
# Get specific fieldsbb pr list --json | jq '.pullRequests[] | {id, title, author: .author.display_name}'
# Filter by authorbb pr list --json | jq '.pullRequests[] | select((.author.nickname // .author.display_name) == "alice")'
# Get PR URLsbb pr list --json | jq -r '.pullRequests[].links.html.href'
# Get web diff URL for a PRbb pr diff 42 --web --json | jq -r '.url'Common jq Patterns
Section titled “Common jq Patterns”# PRs updated in the last 7 daysbb pr list --json | jq --arg date "$(date -d '7 days ago' -Iseconds 2>/dev/null || \ date -v-7d -Iseconds)" \ '.pullRequests[] | select(.updated_on > $date)'
# PRs targeting main branchbb pr list --json | jq '.pullRequests[] | select(.destination.branch.name == "main")'
# Group PRs by authorbb pr list --json | jq '.pullRequests | group_by(.author.nickname // .author.display_name) | map({author: (.[0].author.nickname // .[0].author.display_name), count: length})'Exit Codes
Section titled “Exit Codes”The CLI uses standard exit codes:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (authentication, API, validation, etc.) |
Checking Exit Codes
Section titled “Checking Exit Codes”#!/bin/bash
if bb pr list -w myworkspace -r myrepo > /dev/null 2>&1; then echo "Success"else echo "Failed with exit code: $?"fiError Handling Patterns
Section titled “Error Handling Patterns”#!/bin/bashset -e # Exit on any error
# Or handle errors explicitlybb pr list --json > prs.json || { echo "Failed to list PRs" exit 1}if ! bb config get invalidKey --json > out.json 2> err.json; then jq '.' err.json exit 1fiEnvironment Variables
Section titled “Environment Variables”For non-interactive scripts, use environment variables:
export BB_USERNAME=myuserexport BB_API_TOKEN=ATBB_token
bb auth login # Uses env varsbb pr list -w workspace -r repoSee Environment Variables Reference for details.
Built-in Resilience
Section titled “Built-in Resilience”The CLI handles common transient failures automatically — you don’t need to implement this yourself in most cases.
Automatic Retry
Section titled “Automatic Retry”API requests that fail with transient errors (HTTP 429, 502, 503, 504) are retried automatically up to 3 times with exponential backoff. This means rate-limited or temporarily unavailable responses are handled without any extra scripting.
OAuth Token Refresh
Section titled “OAuth Token Refresh”OAuth access tokens expire after 2 hours. The CLI refreshes them automatically — both proactively (before expiry) and reactively (on 401 responses). Long-running scripts using OAuth don’t need manual re-authentication.
Scripting Best Practices
Section titled “Scripting Best Practices”Always Use Explicit Flags
Section titled “Always Use Explicit Flags”Don’t rely on context detection in scripts:
# Good - explicit and reliablebb pr list -w myworkspace -r myrepo --json
# Bad - may fail if context can't be detectedbb pr list --jsonHandle Persistent Failures
Section titled “Handle Persistent Failures”The CLI retries transient errors automatically (see Built-in Resilience above). For persistent failures, handle the exit code:
#!/bin/bash
if ! bb pr list -w workspace -r repo --json > prs.json 2>error.json; then echo "Failed to list PRs" cat error.json exit 1fiAdd Delays in Loops
Section titled “Add Delays in Loops”When making many sequential API calls, add short delays to avoid hitting rate limits:
#!/bin/bash
for pr_id in $(bb pr list --json | jq -r '.pullRequests[].id'); do bb pr view "$pr_id" --json sleep 1doneExample Scripts
Section titled “Example Scripts”Batch PR Approval
Section titled “Batch PR Approval”Approve all open PRs from a specific author:
#!/bin/bashset -e
WORKSPACE="myworkspace"REPO="myrepo"AUTHOR="trusted-bot"
echo "Finding PRs from $AUTHOR..."
pr_ids=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json | \ jq -r --arg author "$AUTHOR" '.pullRequests[] | select((.author.nickname // .author.display_name) == $author) | .id')
for pr_id in $pr_ids; do echo "Approving PR #$pr_id..." bb pr approve "$pr_id" -w "$WORKSPACE" -r "$REPO" sleep 1done
echo "Done!"PR Status Report
Section titled “PR Status Report”Generate a markdown report of open PRs:
#!/bin/bash
WORKSPACE="myworkspace"REPO="myrepo"
echo "# Open Pull Requests"echo ""echo "Generated: $(date)"echo ""
bb pr list -w "$WORKSPACE" -r "$REPO" --json | jq -r '.pullRequests[] | "## PR #\(.id): \(.title)\n" + "- **Author:** \(.author.display_name)\n" + "- **Branch:** \(.source.branch.name) → \(.destination.branch.name)\n" + "- **Created:** \(.created_on)\n" + "- **Link:** \(.links.html.href)\n"'Auto-Close Stale PRs
Section titled “Auto-Close Stale PRs”Decline PRs not updated in 30 days:
#!/bin/bashset -e
WORKSPACE="myworkspace"REPO="myrepo"DAYS_OLD=30
cutoff=$(date -d "$DAYS_OLD days ago" -Iseconds 2>/dev/null || \ date -v-${DAYS_OLD}d -Iseconds) # macOS fallback
echo "Finding PRs older than $DAYS_OLD days..."
stale_prs=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json | \ jq -r --arg cutoff "$cutoff" '.pullRequests[] | select(.updated_on < $cutoff) | .id')
if [ -z "$stale_prs" ]; then echo "No stale PRs found" exit 0fi
for pr_id in $stale_prs; do echo "Declining stale PR #$pr_id..." bb pr decline "$pr_id" -w "$WORKSPACE" -r "$REPO" sleep 1done