| summary | RepoBar product/tech spec: goals, UX, auth flow, data sources, and platform details. | ||||
|---|---|---|---|---|---|
| read_when |
|
Last updated: 2025-11-24
- macOS menubar-only app (Swift 6.2, Xcode 26) showing selected GitHub repositories with CI state, issues/PR counts, latest release, recent activity, traffic uniques, and a custom blocky commit/activity heatmap.
- Left-click opens rich window; right-click shows classic menu. Uses MenuBarExtraAccess pattern similar to VibeTunnel.
- Login via browser-based OAuth web application flow + PKCE; release tokens are stored in Keychain, while debug builds use file-backed auth storage to avoid local Keychain prompts. Supports GitHub.com and GitHub Enterprise (trusted TLS only). Architecture ready for multi-account but UI surfaces one account.
- Default repo selection: last 5 active repos for the user; user can pin/unpin repos and configure how many show. Refresh interval configurable (1/2/5/15 min, default 5). Launch at login toggle. Sparkle updates.
- No Dock icon; single-instance only.
- Menubar icon: uses menubarextraaccess to differentiate left/right click; by default shows a compact GitHub REST/GraphQL rate-limit meter when data is available, with login/CI aggregate status as fallback/overlay.
- Left-click window: grid of repo cards. Each card includes:
- Repo name + owner, tap to open repo.
- CI status dot (green/red/yellow) with click-through to Checks/Actions page.
- Counts: open issues, open PRs.
- Latest release name + published date (click opens release page).
- Latest activity line (recent issue/PR comment/review).
- Unique visitors & unique cloners (last 14 days).
- Custom blocky heatmap (weekly/day cell style) showing commit/push activity over the last ~180–365 days; uses GitHub-like green scale.
- Context/"…" menu to unpin.
- Repo submenu: open actions, local state, recent lists, heatmap, commits/activity, and inline changelog preview (CHANGELOG.md or CHANGELOG).
- Header (optional): GitHub contribution image (
https://ghchart.rshah.org/<user>) scaled to window width; toggle default ON. - Add repo: "+" button with autocomplete (user/org repos). Pins repo; reorder by activity recency or pin order TBD (start with pin order, recently-added).
- Right-click menu: login state, Log out, Refresh now, Preferences, Check for Updates (Sparkle), Quit. Shows account (GitHub.com or GHE host).
- Settings (Trimmy-style tabs):
- General: number of repos, refresh interval, show contribution image, launch at login, default scope (user vs org?), heatmap on/off (optional).
- Accounts: login state, reconnect, logout; GitHub.com client ID/secret entry; GHE base URL/client ID/secret; loopback port; PEM key import for the GitHub App private key.
- Appearance: card density (compact/comfortable), accent tone (macOS default + GitHub greens for heatmap only).
- Advanced: rate-limit info, cache reset, diagnostics toggle (verbose logging), show ETag/backoff status, local projects folder (auto-sync + terminal picker).
- Info.plist:
LSUIElement= true (no Dock icon)LSMultipleInstancesProhibited= true (single instance)- Custom URL type:
repobarscheme, hostoauth-callback(AppAuth fallback/deep link)
- Bundle identifiers aligned with Trimmy conventions; shared App Group not needed initially.
- GitHub App values (provided): App ID 2344358, Client ID Iv23liGm2arUyotWSjwJ, private key at
/Users/steipete/Library/CloudStorage/Dropbox/Backup/RepoBar/repobar.2025-11-23.private-key.pem. - Redirect URI registered in GitHub App:
http://127.0.0.1:53682/callback(loopback). - AppAuth flow:
- Generate PKCE (S256) + state.
- Open default browser to GitHub authorize URL (web application flow for GitHub Apps) with client_id, redirect_uri, state, code_challenge.
- Local loopback listener on chosen port (default 53682) captures
codeandstate; validate state. - Exchange code + code_verifier + client_secret for access + refresh tokens (user-to-server flow per GitHub App docs).
- Store access/refresh tokens and installation ID via
TokenStore; release builds use Keychain, debug builds use file storage. Cache ETag tokens in a bounded in-memory cache.
- Token refresh: use refresh_token grant; handle 401/403 by retry + reauth prompt.
- GHE: same flow, user provides base URL; trusted certs required (no ATS exceptions).
- GitHub GraphQL v4 via Apollo for primary data:
- Repo basics, issues/PR counts, statusCheckRollup, latest release (first:1), recent timeline items (comments/reviews).
- REST fallbacks via URLSession:
- Actions runs / checks if GraphQL status missing:
GET /repos/{owner}/{repo}/actions/runs?per_page=1. - Traffic:
GET /repos/{owner}/{repo}/traffic/viewsand/traffic/clones(requires Administration: Read permission). - Commit activity for heatmap:
GET /repos/{owner}/{repo}/stats/commit_activity(weekly counts) and/or recent commits with since parameter to build finer day-resolution grid. (These endpoints may cache for minutes; handle 202/empty with backoff.)
- Actions runs / checks if GraphQL status missing:
- Contribution image: simple URL fetch; cache and scale.
- All GitHub REST/GraphQL fetching lives in
RepoBarCore(primarilyGitHubClient+ models). - App/UI code should not add new GitHub network calls directly; instead add a
RepoBarCoreAPI and consume it from the app/CLI.
- GitHub.com OAuth: do not request OAuth scopes. GitHub App user access tokens are limited by the app's installed repository permissions and the signed-in user's access.
- Repository: Metadata (implicit), Contents: Read, Issues: Read, Pull requests: Read, Actions: Read, Checks: Read, Administration: Read (for traffic clones/views), Environments: Read (optional, for richer CI), Commit statuses.
- Organization: none required beyond installation scope; install on orgs to reach private repos.
- Account: none.
- Events: none (polling only).
- Private organization repositories are visible only when the RepoBar GitHub App installation includes that organization/repository, or when the user signs in with a PAT that has
repo+read:org. - GitHub Enterprise custom OAuth apps still request
repo read:orgbecause classic GitHub OAuth has no read-only private repository scope.
- Global refresh interval configurable (1/2/5/15 min; default 5).
- Per-repo throttling with ETag/If-None-Match; exponential backoff on 403 (rate limit) and 202 for stats endpoints.
- Manual "Refresh now" in right-click menu.
- Default view: last 5 active repos (recent pushes/issues/PRs) for the authenticated user (across orgs user can access).
- Settings > Repositories: searchable browser of repositories the authenticated account can access. The browser shows visibility state per repo and lets the user switch each repo between Visible, Pinned, and Hidden.
- Pinning: stored per account. Unpin via card overflow menu or set the repo back to Visible in Settings.
- Display limit configurable in Settings.
- Heatmap: custom SwiftUI grid (rows = weekdays, cols = weeks) using computed intensity buckets from commit counts; GitHub green palette. Keep rendering <500 LOC by extracting helpers.
- CI dot: green/yellow/red/gray (unknown). Click opens checks/actions page.
- Layout: adaptive columns (min card width ~260–300). Use macOS-friendly typography (SF) and system spacing; accent only for heatmap.
- Small loopback HTTP listener for OAuth callback (bound to 127.0.0.1, ephemeral during login only).
- Refresh scheduler using
Task+Timeron main actor for UI updates. - Launch at login via
SMAppService.mainApp.
- Secure release storage: Keychain for access/refresh tokens, client secret, private key.
- Debug storage: debug app bundles set
RepoBarTokenStore=file, and SwiftPM debug CLI/test binaries fall back to file storage in code.TokenStore.sharedstores auth JSON under~/Library/Application Support/RepoBar/DebugAuthinstead of touching Keychain unlessREPOBAR_TOKEN_STORE=keychainis explicitly set. Seedocs/auth-storage.md. - UserDefaults/AppStorage for settings (interval, repo list, show contribution image, launch at login, GHE base URL, port).
- Persistent SQLite cache for ETags, response bodies, recent lists, repo details, and rate-limit state. RepoBar owns GitHub archive source configuration; it must not read gitcrawl config. Git-backed backup archives should follow the Discrawl-style snapshot/import workflow described in
docs/cache.md.
- menubarextraaccess (left/right click support for MenuBarExtra)
- AppAuth (PKCE + browser OAuth helper; custom user agent/loopback)
- Apollo iOS (GraphQL client/codegen) + swift-algorithms
- Sparkle (updates)
- No SwiftUICharts; heatmap is custom.
Package.swiftwith targets: App, Tests.Sources/RepoBar/main app, divided roughly:App/(entry, app/scene, Info helpers)StatusBar/(controller, menu manager, icon controller, custom window)Auth/(PKCE helper, OAuthCoordinator, TokenStore)API/(GitHubClient, GraphQL queries, REST endpoints, mappers)Models/(Repo, Release, CIStatus, Activity, Traffic, HeatmapCell)Views/(Menu window, repo card, heatmap, settings panes)Settings/(tab views, storage)Support/(RefreshScheduler, ImageCache, Logging)
Scripts/copied/adapted from Trimmy:compile_and_run.sh,package_app.sh,sign-and-notarize.sh, lint/format wrappers.Tests/RepoBarTests/for Swift Testing suites.- Tooling:
.swiftformat,.swiftlint.yml, and pnpm scripts (pnpm format,pnpm lint,pnpm check,pnpm test,pnpm build,pnpm start,pnpm restart,pnpm stop).
- PKCE helper (code verifier/challenge correctness).
- Loopback server parsing (query params, state validation, port binding fallback).
- RefreshScheduler intervals and backoff behavior.
- Heatmap binning/color bucketing from weekly/daily stats.
- Mapping of GraphQL release/CI status to UI model.
- Basic integration: mocked GitHub client returning staged responses populates repo card view models.
- Release tokens and secrets use Keychain; debug auth can use file-backed storage. Never log tokens or secrets.
- Debug builds intentionally avoid Keychain via file-backed storage so local autonomous runs do not show macOS Keychain prompts.
- App + CLI share tokens via Keychain access group only when a release build is properly provisioned for that entitlement.
- TLS required (no ATS exceptions); reject self-signed for GHE.
- Minimal scopes; per-installation tokens only.
- Single-instance enforced via Info.plist.
- In GitHub App settings:
- Callback URL:
http://127.0.0.1:53682/callback - Repo permissions: Metadata (default), Contents: Read, Issues: Read, Pull requests: Read, Actions: Read, Checks: Read, Administration: Read (traffic), Environments: Read (optional).
- Expire user tokens: ON. Device Flow: OFF. Events: none. Distribution: Any account.
- Save, note Client ID (Iv23liGm2arUyotWSjwJ), Client Secret (9693b9928c9efd224838e096a147822680983e10), App ID (2344358), and private key path.
- Callback URL:
- In the app (Settings > Accounts): paste Client ID/Secret; import PEM key; leave GHE URL empty unless needed; confirm loopback port 53682.
- Install the App on each org where you need private repos, selecting “All repositories” (or specific ones) so Administration: Read covers traffic endpoints.
- First launch: sign in → browser opens → accept → app captures code on localhost → tokens stored via
TokenStore(Keychain in release, file store in debug).
Done
- Scaffold project: Package.swift deps (Sparkle, MenuBarExtraAccess, AppAuth, Apollo client stub, swift-algorithms); Info.plist flags; Trimmy-style scripts.
- Menubar shell: status bar controller/menu manager/custom window & icon; left/right menus, Sparkle, logout, refresh.
- Auth: custom PKCE + loopback server; Keychain TokenStore; refresh-token flow; host remembered for refresh.
- API client: REST for user/search/full repo (release/CI/activity/traffic/heatmap); ETag + rate-limit tracking; per-repo rate-limit/error surfaced to models/cards.
- Models/views: Repository + RepositoryViewModel; cards show CI/issues/PRs/release/activity/traffic/heatmap, per-repo error/rate-limit; contribution header; empty/error/rate-limit banners; settings panes; add-repo sheet; drag-reorder scaffold for pins.
- Refresh: scheduler with interval/force refresh; settings persistence; pins persisted and ordered.
- Launch/update: launch-at-login helper; Sparkle menu; single-instance enforced.
- Tests: PKCE + heatmap reshape + backoff/refresh/cert error mapping + loopback parser (Swift Testing).
- Error-handling: per-endpoint rate-limit/backoff propagation to cards, repo error/rate-limit copy preserved on reorder; GraphQL enrichment merged with REST.
- UX polish: drag-reorder hints, context-menu move up/down actions, login host surfaced in menus; enterprise host validation + TLS trust messaging; diagnostics section in Advanced settings; custom colored menubar glyph; logged-out state styling and menu container background refined.
- Tooling: swiftformat/swiftlint aligned with Trimmy; pnpm scripts for format/lint/check/test/build/start/restart/stop. Apollo codegen config remains optional; manual GraphQL client is the current default.
- Additional tests: repo view model mapping, heatmap padding, grid reorder helpers.
TODO
- Additional tests (repo model mapping from API, contribution image/heatmap sizing).
- Accessibility: keyboard focus order in menu window, announce rate-limit banners, check card a11y labels.
- Logging/diagnostics toggle and cache reset UI polish.
- Decide whether to re-enable Apollo codegen; regenerate schema/types once a working token for fetch-schema is available.