BarKeeper - Use your macOS Bar to Keep your environments and shortcuts under control.
A lightweight macOS menu bar app for managing resources with one click. Run scripts, monitor the status of applications or spin up and shutdown Cloud Resources — all from your macOS top bar.
- Menu Bar Native — lives in your macOS menu bar, no Dock icon clutter
- Toggle Switches — on/off controls with status monitoring (green/gray/red dots)
- Action Buttons — one-click script execution
- Configurable Polling — automatic status checks (1 min to 1 hour, or manual only)
- Settings UI — add/edit/delete resources with an easy to access UI
- JSON Import/Export — share and backup your configuration in common JSON
- Built-in Azure Fabric Capacity template — get started instantly
- macOS 15.0+ (Sequoia)
- Xcode 16+ / Swift 6.0+
- (Optional for Sample Config) Azure CLI (for Azure resources)
The easiest way to install BarKeeper is to download the pre-built, notarized app from the Releases page:
- Go to the Releases page and download the latest
BarKeeper.dmg. - Open the
.dmgfile and drag BarKeeper.app into your Applications folder. - Eject the disk image and launch BarKeeper from your Applications folder.
The app is signed and notarized by Apple's notarization service, so macOS Gatekeeper will allow it to run without any additional steps.
- macOS 15.0+ (Sequoia)
- Xcode 16+ / Swift 6.0+
- XcodeGen — generates the Xcode project from
project.yml
brew install xcodegenThe Xcode project is not committed to the repo — it is generated by XcodeGen. The DEVELOPMENT_TEAM build setting is resolved from an environment variable so that signing credentials stay out of the source code.
Set your Apple Developer Team ID in your shell profile (~/.bash_profile, ~/.zprofile, or ~/.zshrc):
export DEVELOPMENT_TEAM="YOUR_TEAM_ID"Finding your Team ID: Open Xcode → Settings → Accounts → select your team, or visit Apple Developer — Membership.
If you just want to build without code signing (e.g. for local testing), you can set it to an empty string or skip it — the build will still succeed with ad-hoc signing.
# Clone the repository
git clone https://github.com/abeckDev/BarKeeper.git
cd BarKeeper
# Generate the Xcode project
xcodegen generate
# Option 1: Open in Xcode
open BarKeeper.xcodeproj
# Option 2: Build from command line
xcodebuild build -project BarKeeper.xcodeproj -scheme BarKeeper -configuration DebugNote: You must re-run
xcodegen generatewheneverproject.ymlchanges (e.g. after pulling new commits that modify it).
- Click the BarKeeper icon in your menu bar
- Click Settings → + to add a resource
- Or click ⋯ → Add Fabric Capacity Example for a pre-configured Azure template
- Edit the capacity name and resource group to match your Azure setup
- Close Settings — your resource appears in the menu bar popup
BarKeeper supports two resource types:
A stateful resource with three scripts: status, on, and off. BarKeeper periodically runs the status script to determine the current state and shows a colored indicator in the menu bar.
- Green dot — resource is ON (status script exited with code
0) - Gray dot — resource is OFF (status script exited with non-zero code)
- Red dot — an error occurred during the last action
Clicking the toggle executes the on or off script based on the current state.
A stateless resource with a single action script. Clicking it runs the script immediately. Useful for one-off tasks like flushing a cache, deploying a build, or opening a dashboard.
| Script Type | Exit Code 0 | Non-zero Exit |
|---|---|---|
| Status | Resource is ON (green) | Resource is OFF (gray) |
| On/Off/Action | Success | Error (shown in tooltip) |
Scripts are run using your default login shell ($SHELL, typically /bin/zsh), invoked as:
$SHELL -l -c "<your script>"
- The
-l(login) flag sources your shell profile (~/.zprofile,~/.zshrc), so Homebrew,nvm,pyenv, customPATHentries, and tools likeazCLI are all available. - Scripts inherit your full user environment.
- stdout and stderr are captured separately and trimmed of whitespace.
- The exit code determines success or failure.
Any shell command or script that follows the exit code convention works. Here are a few ideas:
Docker container toggle:
# Status
docker inspect -f '{{.State.Running}}' my-container 2>/dev/null | grep -q true
# On
docker start my-container
# Off
docker stop my-containerSSH tunnel toggle:
# Status
pgrep -f "ssh -N -L 5432:localhost:5432 myserver" > /dev/null
# On
ssh -f -N -L 5432:localhost:5432 myserver
# Off
pkill -f "ssh -N -L 5432:localhost:5432 myserver"Open a dashboard (button):
open "https://portal.azure.com/#view/Dashboard"The built-in template uses these Azure CLI commands:
# Status check
az fabric capacity show --capacity-name "NAME" --resource-group "RG" \
--query "state" -o tsv | grep -qi "Active"
# Start (resume)
az fabric capacity resume --capacity-name "NAME" --resource-group "RG"
# Stop (suspend)
az fabric capacity suspend --capacity-name "NAME" --resource-group "RG"Make sure you're logged in:
az login
Config is stored at ~/Library/Application Support/BarKeeper/config.json and managed through the Settings UI. You can also export/import JSON files for backup or sharing.
{
"pollingIntervalSeconds": 3600,
"resources": [
{
"id": "550E8400-E29B-41D4-A716-446655440000",
"name": "My Toggle",
"type": "toggle",
"statusScript": "docker inspect -f '{{.State.Running}}' my-db | grep -q true",
"onScript": "docker start my-db",
"offScript": "docker stop my-db",
"actionScript": null
},
{
"id": "6BA7B810-9DAD-11D1-80B4-00C04FD5B67D",
"name": "Deploy Staging",
"type": "button",
"statusScript": null,
"onScript": null,
"offScript": null,
"actionScript": "./scripts/deploy-staging.sh"
}
]
}| Field | Type | Description |
|---|---|---|
pollingIntervalSeconds |
Int |
How often to run status scripts for toggles. Default: 3600 (1 hour). Set to 0 for manual-only polling. |
resources |
[Resource] |
Ordered array of resources shown in the menu. |
id |
UUID |
Auto-generated unique identifier. |
name |
String |
Display name shown in the menu bar and settings. |
type |
"button" | "toggle" |
Resource type. |
statusScript |
String? |
Toggle only — script to determine on/off state. |
onScript |
String? |
Toggle only — script to turn the resource on. |
offScript |
String? |
Toggle only — script to turn the resource off. |
actionScript |
String? |
Button only — script to run when clicked. |
- Export: Settings → ⋯ → Export Config saves a
BarKeeper-config.jsonfile you can share or back up. - Import: Settings → ⋯ → Import Config loads a JSON file. This replaces your entire config (it does not merge). All toggle statuses are refreshed immediately after import.
Sources/
├── BarKeeperApp.swift # App entry with MenuBarExtra + Settings scenes
├── Models/
│ ├── Resource.swift # Resource config model (button/toggle)
│ └── ResourceState.swift # Runtime state (@Observable)
├── ViewModels/
│ └── ResourceManager.swift # Config, state, polling, script execution
├── Views/
│ ├── MenuBarView.swift # Menu bar popup
│ ├── ResourceRowView.swift # Toggle / button row
│ ├── SettingsView.swift # Settings window with sidebar
│ └── ResourceEditorView.swift # Resource add/edit form
└── Utilities/
├── ShellExecutor.swift # Async shell command runner
└── ConfigStore.swift # JSON persistence
| Problem | Solution |
|---|---|
| Scripts work in Terminal but not in BarKeeper | Ensure your tool is available in a login shell. Run $SHELL -l -c "which <tool>" to verify. |
az commands fail |
Run az login in your terminal first. BarKeeper inherits your CLI session. |
| Menu bar icon shows an error (❗) | Hover over the resource row to see the error in the tooltip. Fix the script and retry. |
| Status always shows OFF | Make sure your status script exits with code 0 when the resource is on. Test with `<script> && echo ON |
| Config file is corrupted | Delete ~/Library/Application Support/BarKeeper/config.json and relaunch. A fresh default config will be created. |
| Polling feels too frequent/infrequent | Adjust the polling interval in Settings → Polling Interval (1 min to 1 hour, or manual). |
- Scripts run with your full user privileges — BarKeeper does not sandbox script execution. Only configure scripts you trust.
- Config is stored in cleartext — the JSON file at
~/Library/Application Support/BarKeeper/is readable by any process running as your user. Avoid storing secrets directly in scripts; useaz loginsessions, keychains, or environment variables instead. - App Sandbox is disabled — this is required for BarKeeper to execute arbitrary shell commands and access your login shell environment.
BarKeeper is an open-source project provided as-is, without warranty or SLA, under the MIT License. If you run into issues or have questions:
- Ask the community — start a discussion or search existing threads for help.
- Report a bug — Open an Issue to report bugs or request features. Issues serve as our bug tracker.
Contributions are welcome! To get started:
- Fork the repository and clone it
- Set up your environment (see Build from Source)
- Create a feature branch
- Make your changes and verify they compile with
xcodegen generate && xcodebuild build -scheme BarKeeper - Open a Pull Request
| File | Purpose |
|---|---|
project.yml |
XcodeGen spec — the source of truth for the Xcode project |
*.xcodeproj |
Generated & git-ignored — never edit or commit this |
Resources/Info.plist |
Uses build setting variables ($(MARKETING_VERSION), $(CURRENT_PROJECT_VERSION)) — don't hardcode values |
DEVELOPMENT_TEAM |
Resolved from an environment variable at generation time — never committed |
Version and build numbers are managed via build settings in project.yml:
MARKETING_VERSION— App version (e.g.1.0.0), maps toCFBundleShortVersionStringCURRENT_PROJECT_VERSION— Build number (e.g.1), maps toCFBundleVersion
On CI, these are automatically injected from the git tag and GitHub Actions run number — no manual version bumps needed for release
This project is licensed under the MIT License - see the LICENSE file for details.


