Skip to main content

Multi-repo Git dashboard in your macOS menubar — PRs, issues, CI status, and local repo health at a glance.

Project description

gitbar

Multi-repo Git dashboard in your macOS menubar

Monitor PRs, issues, CI failures, and local repo health — all at a glance.

PyPI Python macOS License


What You Get

┌──────────────────────────────────────────┐
│  GP  2❗ • 1✖ • 5                        │  ← menubar with live counts
└──────────────────────────────────────────┘

┌──────────────────────────────────────────┐
│  GitPulse                                │
│──────────────────────────────────────────│
│  👀  NEEDS YOUR REVIEW  (2)             │
│    ✅  #142  Fix auth timeout  — api     │
│    ⏳  #138  Add rate limiting — core    │
│──────────────────────────────────────────│
│  🔁  OPEN PULL REQUESTS  (5)            │
│    ✅  #201  Upgrade deps  — frontend    │
│    ❌  #199  New dashboard — backend     │
│    •   #195  Update README — docs        │
│──────────────────────────────────────────│
│  📋  MY ISSUES  (3)                     │
│    #89  Fix login redirect • bug — app   │
│    #72  Add dark mode • feature — ui     │
│──────────────────────────────────────────│
│  ❌  CI FAILURES  (1)                   │
│    ●  deploy  →  main — backend          │
│──────────────────────────────────────────│
│  💻  LOCAL REPOS                         │
│    myapp      [main]    ● modified • ↑2  │
│    backend    [dev]     ✓ clean          │
│──────────────────────────────────────────│
│  🔓  ACCOUNTS                            │
│    ✅  GitHub connected                  │
│    Connect GitLab...                     │
│    Connect Bitbucket...                  │
│──────────────────────────────────────────│
│  Updated 45s ago                         │
│  ↻  Refresh Now                     ⌘R  │
│  ⚙  Open Config                     ⌘,  │
│  Quit GitPulse                       ⌘Q  │
└──────────────────────────────────────────┘

Quick Start

# 1. Install
pip install gitbar

# 2. Launch
gitbar

# 3. Connect (click GP in menubar → Connect GitHub...)

That's it. GitBar auto-detects your gh CLI login or walks you through a token setup. Your repos are discovered automatically.


Features

Feature Description
Pull Requests See all open PRs across every repo, with CI status indicators
Review Requests PRs waiting for your review, highlighted at the top
Issues Issues assigned to you across all repos
CI Failures Failed GitHub Actions / GitLab pipelines at a glance
Local Repos Dirty files, ahead/behind counts, current branch
Click to Open Click any PR, issue, or CI run to open it in your browser
Notifications Native macOS alerts for new PRs, review requests, CI failures
Multi-Provider GitHub + GitLab + Bitbucket in one view
Secure Tokens stored in macOS Keychain, never in plain text
Keyboard Shortcuts R refresh, , config, Q quit

Architecture

graph TB
    subgraph Menubar["macOS Menubar (PyObjC)"]
        SI["Status Item: GP"]
        Menu["NSMenu + NSMenuDelegate"]
    end

    subgraph Polling["Background Polling"]
        Timer["NSTimer (configurable interval)"]
        Pool["ThreadPoolExecutor (3 workers)"]
    end

    subgraph Providers["API Providers"]
        GH["GitHub REST API v3"]
        GL["GitLab REST API v4"]
        BB["Bitbucket REST API v2"]
    end

    subgraph Data["Data Collected Per Poll"]
        PRs["Open PRs + CI Status"]
        Issues["Assigned Issues"]
        CI["Failed CI Runs"]
        Activity["Recent Activity"]
    end

    subgraph Local["Local Git"]
        Checker["LocalRepoChecker"]
        Git["git subprocess calls"]
    end

    subgraph Storage["Persistence"]
        Config["~/.gitpulse/config.yaml"]
        State["~/.gitpulse/state.json"]
        Keychain["macOS Keychain"]
    end

    subgraph Notify["Notifications"]
        Notifier["Notifier (dedup via StateStore)"]
        macOS["macOS Notification Center"]
    end

    SI --> Menu
    Timer --> Pool
    Pool --> GH & GL & BB
    GH & GL & BB --> PRs & Issues & CI & Activity
    Timer --> Checker
    Checker --> Git
    PRs & Issues & CI & Activity --> Menu
    Activity --> Notifier --> macOS
    Config --> Providers
    Keychain --> Providers
    Notifier --> State

Connection Flow

flowchart TD
    A["Click 'Connect GitHub...'"] --> B{gh CLI token cached?}
    B -->|Yes| C["Show: 'Found gh CLI as username.\nUse this account?'"]
    C -->|Yes, Connect| D["Background thread"]
    C -->|Enter Token Manually| E
    B -->|No| E["Show PAT dialog"]
    E --> F["Open github.com/settings/tokens"]
    F --> G["User pastes token"]
    G --> D
    D --> H["Validate token via API"]
    H -->|Invalid| I["Notification: Invalid token"]
    H -->|Valid| J["Discover all user repos"]
    J --> K["Store token in macOS Keychain"]
    K --> L["Save repos to config.yaml"]
    L --> M["Start polling"]
    M --> N["Menu shows: GitHub ✅"]

    style A fill:#4a9eff,color:#fff
    style N fill:#22c55e,color:#fff
    style I fill:#ef4444,color:#fff

Polling Pipeline

sequenceDiagram
    participant T as NSTimer
    participant P as ThreadPool
    participant GH as GitHub API
    participant LC as LocalRepoChecker
    participant S as StateStore
    participant M as Menu
    participant N as Notifier

    T->>P: poll_once()
    par For each provider
        P->>GH: fetch_open_prs()
        P->>GH: fetch_assigned_issues()
        P->>GH: fetch_ci_runs()
        P->>GH: fetch_recent_activity()
    end
    T->>LC: check_all()
    LC-->>T: LocalRepoStatus[]

    GH-->>P: PollResult (PRs, Issues, CI, Activity)
    P->>S: mark_seen(activity)
    P->>N: maybe_notify(new_pr)
    N-->>N: macOS notification (if new)

    Note over M: User clicks GP icon
    M->>M: menuNeedsUpdate_()
    M->>M: Build sections from cached data

Supported Providers

Feature GitHub GitLab Bitbucket
Open Pull Requests
Review Requests
CI/CD Status ✅ Actions ✅ Pipelines
Assigned Issues
Failed CI Runs
Repo Discovery
Wildcard Repos (org/*)
Keychain Storage
gh CLI Auto-detect
Enterprise / Self-hosted

Configuration

Config is auto-created at ~/.gitpulse/config.yaml when you connect an account. You can also edit it manually:

# How often to poll APIs (seconds, minimum 60)
poll_interval: 300

# Play sound with notifications
notification_sound: true

# Git providers (auto-managed via Connect flow)
providers:
  github:
    token: keychain              # "keychain" = stored in macOS Keychain
    # api_url: https://github.example.com/api/v3   # GitHub Enterprise
    repos:
      - owner/repo1
      - owner/repo2
      - org-name/*               # wildcard: all repos in an org

  gitlab:
    token: keychain
    # api_url: https://gitlab.example.com           # self-hosted
    repos:
      - group/project1
      - group/*

  bitbucket:
    token: keychain
    username: your_username      # required for Bitbucket
    repos:
      - workspace/repo1
      - workspace/*

# Local git repos to monitor
local_repos:
  - ~/code/project-a
  - ~/code/project-b

# Toggle individual notification types
notify:
  new_pr: true
  review_requested: true
  ci_failure: true
  pr_merged: true

How It Works

┌─────────────────────────────────────────────────────────┐
│                    macOS Menubar                         │
│                                                         │
│  PyObjC NSStatusBar → NSMenu with NSMenuDelegate        │
│  Menu rebuilds dynamically every time you click GP      │
│  No timer-based menu updates (avoids macOS UI bugs)     │
└──────────────┬──────────────────────────┬───────────────┘
               │                          │
     ┌─────────▼─────────┐    ┌──────────▼──────────┐
     │  Background Poll   │    │  Local Git Check    │
     │                    │    │                     │
     │  NSTimer fires     │    │  subprocess.run()   │
     │  every N seconds   │    │  git status/log     │
     │  ThreadPoolExec    │    │  per configured     │
     │  (3 workers max)   │    │  repo path          │
     └─────────┬──────────┘    └──────────┬──────────┘
               │                          │
     ┌─────────▼──────────────────────────▼───────────┐
     │              Shared State (thread-safe)         │
     │                                                 │
     │  _current_prs[]    _current_issues[]           │
     │  _current_ci_runs[]  _local_statuses[]         │
     │  Protected by threading.Lock()                  │
     └─────────┬──────────────────────────┬───────────┘
               │                          │
     ┌─────────▼─────────┐    ┌──────────▼──────────┐
     │  Notifier          │    │  StateStore         │
     │  Dedup via seen IDs│    │  JSON persistence   │
     │  macOS native      │    │  LRU (5000 max)     │
     │  notifications     │    │  Debounced flush    │
     └───────────────────┘    └─────────────────────┘

Key design decisions:

  • PyObjC instead of rumps — rumps has compatibility issues on macOS 26+. Native PyObjC gives full control.
  • NSMenuDelegate pattern — Menu content rebuilds on every open (menuNeedsUpdate_), so it's always fresh without timer-based UI updates.
  • All dialogs in main thread — NSAlert/NSTextField only from menu action callbacks, never from timers or background threads.
  • Background work via threading — API calls and git checks run in daemon threads; only notify() is called from background (via osascript, which is thread-safe).
  • macOS Keychain for tokens — Uses the security CLI tool. Config stores token: keychain as a sentinel.

Development

# Clone
git clone https://github.com/vamsi876/gitbar.git
cd gitbar

# Install in dev mode
pip install -e .

# Run
gitbar
# or
python run.py

Project Structure

gitbar/
├── gitpulse/                  # main package
│   ├── app.py                 # PyObjC menubar app + menu building
│   ├── auth.py                # GitHub/GitLab/Bitbucket auth + keychain
│   ├── config.py              # YAML config loader/saver
│   ├── models.py              # Dataclasses: PullRequest, Issue, CIRun, etc.
│   ├── ui.py                  # Native macOS dialogs (NSAlert, notifications)
│   ├── poller.py              # Concurrent polling scheduler
│   ├── notifier.py            # Notification dedup + delivery
│   ├── state.py               # Persistent state (seen events, last poll)
│   ├── localrepo.py           # Local git repo status checker
│   └── providers/
│       ├── base.py            # Abstract provider with rate-limit handling
│       ├── github.py          # GitHub REST API v3
│       ├── gitlab.py          # GitLab REST API v4
│       └── bitbucket.py       # Bitbucket REST API v2
├── run.py                     # Dev entry point
├── config.example.yaml        # Config template
├── pyproject.toml             # Package config (PyPI name: gitbar)
├── requirements.txt
├── LICENSE
└── README.md

Requirements

  • macOS 13+ (Ventura or later)
  • Python 3.10+

License

MIT — see LICENSE

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitbar-0.1.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitbar-0.1.0-py3-none-any.whl (29.5 kB view details)

Uploaded Python 3

File details

Details for the file gitbar-0.1.0.tar.gz.

File metadata

  • Download URL: gitbar-0.1.0.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for gitbar-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7777fb800ed757683cd335a56b7620755e77ab4b1df6e4d6950f8ea9f137967e
MD5 32104d49b9fe2db57cee1e89c4b8dd7b
BLAKE2b-256 c64eb1f402dafe1405a98117473ab5aa6cf57cc41b18ee8562506e4a48656a35

See more details on using hashes here.

File details

Details for the file gitbar-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gitbar-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for gitbar-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6e9a51be1df837eee3e9c816ee01b0844fbd98ae075c3aad7063460a0b8b8f3a
MD5 7a6a9463c9b1f457bdda1bbbd05cf7c5
BLAKE2b-256 7194fb29362f93ebf4186f438ef844375eeb9bd74834590b9070577ca99df92c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page