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.
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
securityCLI tool. Config storestoken: keychainas 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7777fb800ed757683cd335a56b7620755e77ab4b1df6e4d6950f8ea9f137967e
|
|
| MD5 |
32104d49b9fe2db57cee1e89c4b8dd7b
|
|
| BLAKE2b-256 |
c64eb1f402dafe1405a98117473ab5aa6cf57cc41b18ee8562506e4a48656a35
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e9a51be1df837eee3e9c816ee01b0844fbd98ae075c3aad7063460a0b8b8f3a
|
|
| MD5 |
7a6a9463c9b1f457bdda1bbbd05cf7c5
|
|
| BLAKE2b-256 |
7194fb29362f93ebf4186f438ef844375eeb9bd74834590b9070577ca99df92c
|