Scripting & Automation
The Bitbucket CLI is designed for both interactive use and automation. This guide covers JSON output, exit codes, and scripting patterns.
JSON Output Mode
All commands support the --json flag for machine-readable output:
bb pr list --jsonbb repo list --jsonbb pr view 42 --jsonParsing with jq
jq is the standard tool for processing JSON:
# List all PR titlesbb pr list --json | jq '.[].title'
# Get PR countbb pr list --json | jq 'length'
# Filter by statebb pr list -s MERGED --json | jq 'length'
# Get specific fieldsbb pr list --json | jq '.[] | {id, title, author: .author.display_name}'
# Filter by authorbb pr list --json | jq '.[] | select(.author.username == "alice")'
# Get PR URLsbb pr list --json | jq -r '.[].links.html.href'Common jq Patterns
# PRs updated in the last 7 daysbb pr list --json | jq --arg date "$(date -d '7 days ago' -Iseconds)" \ '.[] | select(.updated_on > $date)'
# PRs targeting main branchbb pr list --json | jq '.[] | select(.destination.branch.name == "main")'
# Group PRs by authorbb pr list --json | jq 'group_by(.author.username) | map({author: .[0].author.username, count: length})'Exit Codes
The CLI uses standard exit codes:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (authentication, API, validation, etc.) |
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
#!/bin/bashset -e # Exit on any error
# Or handle errors explicitlybb pr list --json > prs.json || { echo "Failed to list PRs" exit 1}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.
Scripting Best Practices
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 API Failures
Implement retry logic for transient failures:
#!/bin/bash
max_retries=3retry_delay=5
for ((i=1; i<=max_retries; i++)); do if bb pr list -w workspace -r repo --json > prs.json 2>&1; then break fi
if [ $i -lt $max_retries ]; then echo "Attempt $i failed, retrying in ${retry_delay}s..." sleep $retry_delay else echo "All attempts failed" exit 1 fidoneRespect Rate Limits
Add delays between requests:
#!/bin/bash
for pr_id in $(bb pr list --json | jq -r '.[].id'); do bb pr view "$pr_id" --json sleep 1 # Rate limit protectiondoneExample Scripts
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" '.[] | select(.author.username == $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
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 '.[] | "## 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
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" '.[] | 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 1doneRepository Backup Script
Clone all repositories in a workspace:
#!/bin/bashset -e
WORKSPACE="myworkspace"BACKUP_DIR="./bitbucket-backup-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"cd "$BACKUP_DIR"
echo "Fetching repository list..."repos=$(bb repo list -w "$WORKSPACE" --json | jq -r '.[].name')
for repo in $repos; do echo "Cloning $repo..." bb repo clone "$WORKSPACE/$repo" || echo "Failed to clone $repo" sleep 1done
echo "Backup complete: $BACKUP_DIR"PR Diff Analysis
Analyze changes across all open PRs:
#!/bin/bash
WORKSPACE="myworkspace"REPO="myrepo"
echo "Analyzing open PRs..."echo ""
total_files=0total_additions=0total_deletions=0
for pr_id in $(bb pr list -w "$WORKSPACE" -r "$REPO" --json | jq -r '.[].id'); do stats=$(bb pr diff "$pr_id" -w "$WORKSPACE" -r "$REPO" --stat --json 2>/dev/null)
if [ -n "$stats" ]; then files=$(echo "$stats" | jq '.stat.filesChanged // 0') additions=$(echo "$stats" | jq '.stat.insertions // 0') deletions=$(echo "$stats" | jq '.stat.deletions // 0')
echo "PR #$pr_id: $files files, +$additions -$deletions"
total_files=$((total_files + files)) total_additions=$((total_additions + additions)) total_deletions=$((total_deletions + deletions)) fi
sleep 1done
echo ""echo "Total: $total_files files, +$total_additions -$total_deletions"Python Integration
import subprocessimport json
def run_bb(args): """Run bb command and return parsed JSON output.""" result = subprocess.run( ['bb'] + args + ['--json'], capture_output=True, text=True ) if result.returncode != 0: raise Exception(f"bb command failed: {result.stderr}") return json.loads(result.stdout)
# List PRsprs = run_bb(['pr', 'list', '-w', 'workspace', '-r', 'repo'])for pr in prs: print(f"#{pr['id']}: {pr['title']}")
# Get PR detailspr = run_bb(['pr', 'view', '42', '-w', 'workspace', '-r', 'repo'])print(f"State: {pr['state']}")Node.js Integration
import { execSync } from 'child_process';
function runBB(args) { const result = execSync(`bb ${args.join(' ')} --json`, { encoding: 'utf8' }); return JSON.parse(result);}
// List PRsconst prs = runBB(['pr', 'list', '-w', 'workspace', '-r', 'repo']);prs.forEach(pr => { console.log(`#${pr.id}: ${pr.title}`);});