Runbook

Audit All Bitbucket Repositories in Your Workspace

Generate a comprehensive CSV report of every repository in your Bitbucket workspace. Includes size, language, visibility, permissions, and activity dates for compliance and cleanup.

What This Does

This runbook fetches all repositories in a Bitbucket workspace and exports a detailed CSV audit report. Each row includes the repository name, size in megabytes, primary language, visibility, fork status, feature flags, creation and last-update dates, default branch, and permission count. A summary with totals and a language breakdown is printed to the terminal after the CSV is written.

Prerequisites

Quick Start

# Audit all repos with default output file
./repo-audit.sh myworkspace

# Use a specific profile and output file
./repo-audit.sh myworkspace prod repos_audit.csv

# Quick one-liner with atlassian-cli directly
atlassian-cli bitbucket repo list myworkspace --output json | \
  jq -r '.[] | [.slug, .language, .size] | @csv'

Full Runbook Script

repo-audit.sh

#!/bin/bash
# Bitbucket Repository Audit Script
#
# Generates a comprehensive CSV audit report of all repositories in a workspace.
# Includes repository details, permissions, and activity metrics.
#
# Usage:
#   ./repo-audit.sh --workspace myworkspace --output repos.csv
#   ./repo-audit.sh --workspace myworkspace --profile prod
#
# Output CSV columns:
#   - Repo Name, Full Name, Size, Language, Public, Fork, Has Wiki,
#     Has Issues, Created, Updated, Default Branch
#
# Requirements:
#   - atlassian-cli installed and configured
#   - jq for JSON processing

set -euo pipefail

# Configuration
WORKSPACE="${1:?Workspace slug required}"
PROFILE="${2:-default}"
OUTPUT_FILE="${3:-repos_audit_$(date +%Y%m%d).csv}"

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() {
    echo -e "${GREEN}[$(date +'%H:%M:%S')]${NC} $*"
}

warn() {
    echo -e "${YELLOW}[WARN]${NC} $*"
}

# Get all repositories
get_all_repos() {
    log "Fetching all repositories from workspace: $WORKSPACE"

    atlassian-cli bitbucket repo list \
        --profile "$PROFILE" \
        "$WORKSPACE" \
        --output json
}

# Get repository permissions
get_repo_permissions() {
    local repo="$1"

    atlassian-cli bitbucket permissions list \
        --profile "$PROFILE" \
        "$WORKSPACE" \
        "$repo" \
        --output json 2>/dev/null || echo "[]"
}

# Generate CSV report
generate_report() {
    local repos="$1"

    log "Generating audit report: $OUTPUT_FILE"

    # Write CSV header
    echo "Repo Name,Full Name,Size (MB),Language,Public,Fork,Has Wiki,Has Issues,Created,Updated,Default Branch,Permission Count" > "$OUTPUT_FILE"

    local total
    total=$(echo "$repos" | jq '. | length')
    local processed=0

    echo "$repos" | jq -r '.[] | @json' | while IFS= read -r repo_json; do
        local name
        name=$(echo "$repo_json" | jq -r '.slug')
        local full_name
        full_name=$(echo "$repo_json" | jq -r '.full_name' | sed 's/"/""/g')
        local size
        size=$(echo "$repo_json" | jq -r '.size // 0')
        # Convert bytes to MB
        size=$((size / 1024 / 1024))
        local language
        language=$(echo "$repo_json" | jq -r '.language // "N/A"')
        local is_public
        is_public=$(echo "$repo_json" | jq -r '.is_private | if . then "No" else "Yes" end')
        local is_fork
        is_fork=$(echo "$repo_json" | jq -r 'if has("parent") then "Yes" else "No" end')
        local has_wiki
        has_wiki=$(echo "$repo_json" | jq -r '.has_wiki // false | if . then "Yes" else "No" end')
        local has_issues
        has_issues=$(echo "$repo_json" | jq -r '.has_issues // false | if . then "Yes" else "No" end')
        local created
        created=$(echo "$repo_json" | jq -r '.created_on')
        local updated
        updated=$(echo "$repo_json" | jq -r '.updated_on')
        local default_branch
        default_branch=$(echo "$repo_json" | jq -r '.mainbranch.name // "N/A"')

        processed=$((processed + 1))
        log "Processing [$processed/$total]: $name"

        # Get permissions count
        local permissions
        permissions=$(get_repo_permissions "$name")
        local perm_count
        perm_count=$(echo "$permissions" | jq '. | length')

        # Write CSV row
        echo "\"$name\",\"$full_name\",$size,\"$language\",$is_public,$is_fork,$has_wiki,$has_issues,\"$created\",\"$updated\",\"$default_branch\",$perm_count" >> "$OUTPUT_FILE"
    done
}

# Generate summary statistics
generate_summary() {
    log "Generating summary statistics"

    local total_repos
    total_repos=$(tail -n +2 "$OUTPUT_FILE" | wc -l | tr -d ' ')

    local total_size
    total_size=$(tail -n +2 "$OUTPUT_FILE" | cut -d',' -f3 | \
        awk '{sum+=$1} END {print int(sum)}')

    local public_repos
    public_repos=$(tail -n +2 "$OUTPUT_FILE" | cut -d',' -f5 | \
        grep -c "Yes" || echo "0")

    local forks
    forks=$(tail -n +2 "$OUTPUT_FILE" | cut -d',' -f6 | \
        grep -c "Yes" || echo "0")

    echo ""
    log "Audit Summary:"
    echo "  Total Repositories: $total_repos"
    echo "  Public Repositories: $public_repos"
    echo "  Forks: $forks"
    echo "  Total Size: ${total_size} MB"
    echo ""

    # Language breakdown
    log "Repositories by Language:"
    tail -n +2 "$OUTPUT_FILE" | cut -d',' -f4 | \
        sed 's/"//g' | sort | uniq -c | \
        awk '{printf "  %s: %d\n", $2, $1}'

    echo ""
}

# Main execution
main() {
    log "Starting repository audit"
    log "Workspace: $WORKSPACE | Profile: $PROFILE"

    local repos
    repos=$(get_all_repos)

    if [ "$(echo "$repos" | jq '. | length')" -eq 0 ]; then
        warn "No repositories found in workspace: $WORKSPACE"
        exit 0
    fi

    generate_report "$repos"
    generate_summary

    log "Audit report saved to: $OUTPUT_FILE"
}

main

How It Works

1

Configure inputs. The script takes the workspace slug as a required first argument, with optional profile (default: default) and output filename (default: repos_audit_YYYYMMDD.csv) as positional arguments.

2

Fetch all repositories. A single call to bitbucket repo list retrieves every repository in the workspace as JSON. The script validates that at least one repository exists before proceeding.

3

Extract metadata per repo. For each repository, the script parses slug, full name, size (converted from bytes to MB), language, visibility, fork status, wiki/issues flags, creation date, last update date, and default branch from the JSON response.

4

Collect permissions. Each repository's permission list is queried separately to count the number of user/group permission entries. This helps identify repos with overly broad or missing access controls.

5

Generate CSV and summary. All data is written to a CSV file with headers. A terminal summary shows total repositories, public count, fork count, total size in MB, and a breakdown of repositories by primary language.

Related Runbooks

Copied!