Reporting & analytics with jq
The Scripting guide lists three starter jq patterns. This recipe expands that into a small analytics toolkit you can paste into a weekly report job.
All recipes assume:
WORKSPACE=myworkspaceREPO=myrepoCount PRs per state
Section titled “Count PRs per state”bb pr list only returns one state at a time, so to count across all states fetch each separately and combine:
for state in OPEN MERGED DECLINED SUPERSEDED; do count=$(bb pr list -w "$WORKSPACE" -r "$REPO" -s "$state" --json --jq '.count') printf "%-12s %s\n" "$state" "$count"doneSample output:
OPEN 12MERGED 340DECLINED 8SUPERSEDED 2If you only need a snapshot of currently open PRs grouped by state attribute, a one-liner suffices:
bb pr list -w "$WORKSPACE" -r "$REPO" --json --jq ' .pullRequests | group_by(.state) | map({ state: .[0].state, count: length })'Sum additions and deletions across PRs
Section titled “Sum additions and deletions across PRs”pr list does not include diff stats — they come from bb pr diff <id> --json --stat (or equivalent). For aggregate volume, walk every PR and sum:
#!/bin/bashtotal_added=0total_deleted=0
for pr_id in $(bb pr list -w "$WORKSPACE" -r "$REPO" -s MERGED --json \ --jq '.pullRequests[].id'); do stats=$(bb pr diff "$pr_id" -w "$WORKSPACE" -r "$REPO" --stat --json) added=$(echo "$stats" | jq '[.files[].additions] | add // 0') deleted=$(echo "$stats" | jq '[.files[].deletions] | add // 0') total_added=$(( total_added + added )) total_deleted=$(( total_deleted + deleted )) sleep 1done
echo "Total added: $total_added"echo "Total deleted: $total_deleted"Group by author with counts
Section titled “Group by author with counts”bb pr list -w "$WORKSPACE" -r "$REPO" --json --jq ' .pullRequests | group_by(.author.nickname // .author.display_name) | map({ author: (.[0].author.nickname // .[0].author.display_name), count: length, ids: [.[].id] }) | sort_by(-.count)'This sorts authors by descending PR count and includes the IDs for drilldown.
Top reviewers across merged PRs
Section titled “Top reviewers across merged PRs”Useful for identifying review load. Iterates over merged PRs and counts approvals per reviewer:
bb pr list -w "$WORKSPACE" -r "$REPO" -s MERGED --limit 200 --json --jq ' [ .pullRequests[].participants[] | select(.approved == true) | .user.nickname // .user.display_name ] | group_by(.) | map({ reviewer: .[0], approvals: length }) | sort_by(-.approvals)'CSV export
Section titled “CSV export”To export an open-PRs report into a spreadsheet, use jq’s @csv:
bb pr list -w "$WORKSPACE" -r "$REPO" --limit 500 --json --jq ' ["id","title","author","state","created_on","source","destination"], ( .pullRequests[] | [ .id, .title, (.author.nickname // .author.display_name), .state, .created_on, .source.branch.name, .destination.branch.name ] ) | @csv' > prs.csv
head -3 prs.csvSample output:
"id","title","author","state","created_on","source","destination"42,"Add login button","alice","OPEN","2025-01-04T09:11:00Z","feat/login","main"43,"Fix typo in README","bob","OPEN","2025-01-04T11:22:00Z","fix/typo","main"PR cycle time (created → merged)
Section titled “PR cycle time (created → merged)”bb pr list -w "$WORKSPACE" -r "$REPO" -s MERGED --limit 100 --json --jq ' [ .pullRequests[] | { id, title, hours: ( (.updated_on | fromdateiso8601) - (.created_on | fromdateiso8601) ) / 3600 } ] | sort_by(.hours)'updated_on on a merged PR approximates the merge time; for exact merge time, walk bb pr activity <id> and find the merge event.
Related
Section titled “Related”- Scripting & Automation guide — JSON output, exit codes, primitive jq patterns
- JSON Output reference — the envelope shape for list-style commands
bb pr list— flags like-s,--limit,--json,--jq