Metadata-Version: 2.4
Name: gitbar
Version: 0.1.0
Summary: Multi-repo Git dashboard in your macOS menubar — PRs, issues, CI status, and local repo health at a glance.
Author: Vamsi Krishna Kollipara
License: MIT
Project-URL: Homepage, https://github.com/vamsi876/gitbar
Project-URL: Issues, https://github.com/vamsi876/gitbar/issues
Keywords: git,github,gitlab,bitbucket,menubar,macos,dashboard,pull-request
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: MacOS X
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: pyobjc-framework-Cocoa>=10.0
Dynamic: license-file

<p align="center">
  <h1 align="center">gitbar</h1>
  <p align="center">
    <strong>Multi-repo Git dashboard in your macOS menubar</strong>
  </p>
  <p align="center">
    Monitor PRs, issues, CI failures, and local repo health — all at a glance.
  </p>
  <p align="center">
    <a href="https://pypi.org/project/gitbar/"><img src="https://img.shields.io/pypi/v/gitbar?color=blue" alt="PyPI"></a>
    <a href="https://pypi.org/project/gitbar/"><img src="https://img.shields.io/pypi/pyversions/gitbar" alt="Python"></a>
    <img src="https://img.shields.io/badge/platform-macOS-lightgrey" alt="macOS">
    <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
  </p>
</p>

---

## 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

```bash
# 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

```mermaid
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

```mermaid
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

```mermaid
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:

```yaml
# 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

```bash
# 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 &mdash; see [LICENSE](LICENSE)
