buo/homebrew-cask-upgrade
A command line tool for upgrading every outdated app installed by Homebrew Cask
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3d ago
- ✓10 active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ⚠Concentrated ownership — top contributor handles 70% of recent commits
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/buo/homebrew-cask-upgrade)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/buo/homebrew-cask-upgrade on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: buo/homebrew-cask-upgrade
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/buo/homebrew-cask-upgrade shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 3d ago
- 10 active contributors
- MIT licensed
- CI configured
- ⚠ Concentrated ownership — top contributor handles 70% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live buo/homebrew-cask-upgrade
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/buo/homebrew-cask-upgrade.
What it runs against: a local clone of buo/homebrew-cask-upgrade — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in buo/homebrew-cask-upgrade | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of buo/homebrew-cask-upgrade. If you don't
# have one yet, run these first:
#
# git clone https://github.com/buo/homebrew-cask-upgrade.git
# cd homebrew-cask-upgrade
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of buo/homebrew-cask-upgrade and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "buo/homebrew-cask-upgrade(\\.git)?\\b" \\
&& ok "origin remote is buo/homebrew-cask-upgrade" \\
|| miss "origin remote is not buo/homebrew-cask-upgrade (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/bcu.rb" \\
&& ok "lib/bcu.rb" \\
|| miss "missing critical file: lib/bcu.rb"
test -f "lib/bcu/command/command.rb" \\
&& ok "lib/bcu/command/command.rb" \\
|| miss "missing critical file: lib/bcu/command/command.rb"
test -f "lib/bcu/options.rb" \\
&& ok "lib/bcu/options.rb" \\
|| miss "missing critical file: lib/bcu/options.rb"
test -f "lib/extend/cask.rb" \\
&& ok "lib/extend/cask.rb" \\
|| miss "missing critical file: lib/extend/cask.rb"
test -f "lib/bcu/module/pin.rb" \\
&& ok "lib/bcu/module/pin.rb" \\
|| miss "missing critical file: lib/bcu/module/pin.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/buo/homebrew-cask-upgrade"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
brew-cask-upgrade is a Ruby CLI tool that automates interactive upgrading of outdated macOS applications installed via Homebrew Cask. It replaces the native brew upgrade command by adding interactivity, glob pattern matching, version pinning, and the ability to skip apps with auto-update functionality—solving the problem of managing dozens of GUI apps efficiently on macOS. Modular command-driven architecture: lib/bcu.rb is the main entry point, lib/bcu/command/ contains discrete commands (upgrade.rb, all.rb, livecheck.rb, pin_*.rb), lib/bcu/module/ holds shared logic (pin.rb), and lib/extend/ monkey-patches Ruby core classes (Cask, Version, Formatter). Entry point is bin/brew-cask-upgrade which gets symlinked as brew cu by Homebrew.
👥Who it's for
macOS power users and developers who use Homebrew Cask to manage multiple GUI applications and want a faster, more interactive way to upgrade them all at once, with fine-grained control over which apps to upgrade and which to pin.
🌱Maturity & risk
Actively maintained production-ready tool with CI/CD pipelines (GitHub Actions workflows for CI and style checking), Ruby static analysis setup, and a clear command structure. The codebase is well-organized with ~35KB of Ruby code, but lacks visible test coverage metrics—suggests functional maturity but potential testing gaps.
Single-language dependency on Ruby and Homebrew ecosystem means breaking changes in Homebrew's Cask API could impact functionality. The repository appears actively maintained (evidenced by CI setup) but is not part of the official Homebrew organization, creating maintenance risk if the primary maintainer becomes unavailable. No visible dependency lock file (Gemfile.lock) in the file list raises concerns about reproducible installations.
Active areas of work
Repository shows active CI/CD setup with Dependabot dependency management (.github/dependabot.yml) and automated style checking. The presence of pin management commands and livecheck integration suggests recent feature work around version pinning and upstream version detection.
🚀Get running
git clone https://github.com/buo/homebrew-cask-upgrade.git
cd homebrew-cask-upgrade
bundle install
bin/brew-cask-upgrade --help
Or for development against the tap: brew tap buo/cask-upgrade then brew cu --help.
Daily commands:
For CLI testing: bin/brew-cask-upgrade [command] [cask] [options]. For Homebrew integration after tapping: brew cu [cask]. Development: bundle exec bin/brew-cask-upgrade --help to bypass Homebrew's symlink layer.
🗺️Map of the codebase
lib/bcu.rb— Main entry point and orchestrator for the entire upgrade tool; all commands and modules are initialized here.lib/bcu/command/command.rb— Abstract base class for all commands; defines the command interface and execution pattern used throughout the tool.lib/bcu/options.rb— Centralized CLI argument and option parsing; critical for understanding how user input flows through the application.lib/extend/cask.rb— Monkey-patches Homebrew Cask to add upgrade detection and pinning logic; core domain logic for identifying outdated casks.lib/bcu/module/pin.rb— Pin management system; handles persistent storage and retrieval of pinned casks across upgrade operations.bin/brew-cask-upgrade— Executable entry point; bootstraps the CLI and delegates to the command dispatcher.brew-cask-upgrade.gemspec— Gem manifest defining dependencies and metadata; required to understand external integrations and build configuration.
🧩Components & responsibilities
- CLI Entry (bin/brew-cask-upgrade) (Ruby, Homebrew integration) — Parse command line, bootstrap BCC module, hand off to command dispatcher
- Failure mode: If entry point fails, 'brew cu' command is not accessible
- Command Router (lib/bcu.rb) (Ruby, Command pattern) — Instantiate correct command class based on user args; coordinate execution and output
- Failure mode: If router fails, no command executes; user sees error instead of help
- Upgrade Command (lib/bcu/command/upgrade.rb) (Homebrew Cask API, Interactive I/O) — Fetch outdated casks, filter by pins, prompt user, execute Homebrew upgrade
- Failure mode: If upgrade fails, casks may be partially upgraded or user input lost
- Cask Extension (lib/extend/cask.rb) (Ruby monkey-patching, Homebrew Cask internals) — Add outdated?, pinned?, and version comparison methods to Cask class
- Failure mode: If Homebrew Cask API changes, extension breaks; tool becomes non-functional
- Pin Module (lib/bcu/module/pin.rb) (Ruby File I/O, YAML/JSON) — Persist and retrieve pinned cask list from filesystem
- Failure mode: If pin file is corrupted or inaccessible, pinning feature fails silently or errors
🔀Data flow
User terminal input→lib/bcu/options.rb— CLI args parsed by OptionParser into Ruby hashlib/bcu.rb→undefined— undefined
🛠️How to make changes
Add a new command (e.g., brew cu status)
- Create a new command class inheriting from Command base class (
lib/bcu/command/new_command.rb) - Implement execute() method with your logic (
lib/bcu/command/new_command.rb) - Register the command in the dispatcher by instantiating it (
lib/bcu.rb) - Add corresponding OptionParser flag if needed (
lib/bcu/options.rb)
Extend Cask behavior (e.g., add custom upgrade detection)
- Add a new method to the Cask monkey-patch module (
lib/extend/cask.rb) - Use it in upgrade or livecheck commands (
lib/bcu/command/upgrade.rb)
Modify pin storage format or location
- Update file path constant or add new storage backend methods (
lib/bcu/module/pin.rb) - Ensure all pin commands (add/remove/list/export/load) read from new source (
lib/bcu/command/pin_*.rb)
Add version comparison logic
- Enhance version normalization or comparison methods (
lib/extend/version.rb) - Use in Cask.outdated? checks (
lib/extend/cask.rb)
🔧Why these technologies
- Ruby + Homebrew CLI integration — Homebrew itself is Ruby-based; this allows seamless integration as a native Homebrew command (brew cu) and direct manipulation of Cask objects
- Monkey-patching (lib/extend/) — Extends Homebrew Cask without forking; adds outdated detection and version comparison directly to Cask class
- File-based pin persistence (YAML/JSON in pin.rb) — Simple, portable, human-readable storage for pinned casks; no external DB required for a local-only tool
- Command pattern (lib/bcu/command/) — Cleanly separates concerns (upgrade vs. pin vs. livecheck); enables easy addition of new subcommands
⚖️Trade-offs already made
-
Interactive CLI vs. scripting-friendly output
- Why: Homebrew GUI app upgrades benefit from user confirmation to prevent unexpected breaks
- Consequence: Non-interactive batch mode requires explicit --force flag; slightly slower for automation
-
Monkey-patching Cask vs. wrapping/adapting
- Why: Monkey-patching is minimal code; wrapping would require reimplementing Cask internals
- Consequence: Breaking changes in Homebrew Cask API directly impact this tool; tight coupling
-
File-based pin storage vs. environment variables
- Why: File persistence survives terminal sessions; human-editable for advanced users
- Consequence: Pin operations require filesystem I/O; file corruption could break pinning feature
🚫Non-goals (don't propose these)
- Does not manage non-Cask Homebrew packages (formulae); cask-specific only
- Does not handle authentication or privilege escalation; assumes user has sudo access if needed
- Does not work on Linux or Windows; macOS-specific due to Homebrew Cask scope
- Does not provide real-time version monitoring or scheduled upgrades; manual/one-shot tool only
- Does not support native Homebrew package managers other than Cask (e.g., does not upgrade Homebrew core or taps)
🪤Traps & gotchas
Homebrew Cask's internal API is not officially stable—updates to Homebrew can break version detection or cask metadata access without warning. Pin data is persisted somewhere (likely ~/.homebrew/ or similar) but the storage path is not visible in the file list—modifying pin logic without understanding the storage backend will cause issues. Glob pattern matching happens at the Ruby level but must be quoted/escaped to prevent shell expansion, which confuses users coming from traditional command-line tools. The tool assumes brew update is available and succeeds; offline or network-restricted environments will fail silently.
🏗️Architecture
💡Concepts to learn
- Glob pattern matching — The README shows this tool supports glob patterns (* ? [abc]) to upgrade multiple casks at once, but users must quote them to prevent shell expansion—understanding glob semantics is critical to avoid user confusion about why unquoted patterns fail
- Monkey-patching / Open Classes — The entire lib/extend/ directory relies on Ruby's ability to reopen and extend existing classes (Cask, Version, Formatter) at runtime; this is the core technique for adding upgrade-specific behavior without forking Homebrew's code
- Command pattern — The tool implements a hierarchy of command classes (upgrade.rb, pin_add.rb, all.rb inherit from command.rb) allowing each subcommand to encapsulate its own logic and options—essential for understanding how to add new commands
- Version comparison and semver — lib/extend/version.rb extends version comparison logic to determine if a cask is outdated; understanding how semver parsing works in Homebrew is critical for debugging upgrade detection failures
- Persistent state management without a database — The pin module stores user preferences (which casks are pinned) to disk without a traditional database—understanding file-based state persistence patterns is needed to modify or debug the pinning feature
- Interactive CLI prompts and user confirmation — The upgrade.rb command drives an interactive workflow asking users which casks to upgrade; understanding how to handle user input, confirmations, and cancellations is essential for modifying upgrade behavior
- API wrapper / adapter pattern — The entire project wraps Homebrew Cask's CLI and internal APIs with a friendlier interface; understanding how lib/extend/ adapts Homebrew's classes for upgrade-specific use cases is core to extending functionality
🔗Related repos
Homebrew/homebrew-cask— The official Homebrew Cask repository that manages the actual cask definitions and upgrade metadata this tool queriesmas-cli/mas— Similar tool for upgrading Mac App Store apps; complements brew-cask-upgrade for complete macOS app managementrcmdnk/homebrew-file— Homebrew package manager for brewfiles; users of brew-cask-upgrade often combine it with brewfile management for reproducible setupsHomebrew/homebrew-bundle— Official Homebrew companion tool for managing cask lists in Brewfiles; integrates with the cask ecosystem that brew-cask-upgrade targets
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for lib/bcu/command/*.rb classes
The repo has multiple command classes (all.rb, upgrade.rb, livecheck.rb, pin_*.rb) but there's no visible test directory in the file structure. This is critical for a tool that modifies user systems. New contributors can add RSpec tests for each command class to ensure upgrade logic, pin management, and livecheck functionality work correctly before users run them.
- [ ] Create spec/ directory structure mirroring lib/bcu/command/
- [ ] Add spec/bcu/command/upgrade_spec.rb with tests for upgrade flow (detecting outdated casks, filtering pinned ones)
- [ ] Add spec/bcu/command/pin_*.rb tests for add/remove/list/load/export functionality
- [ ] Add spec/bcu/command/livecheck_spec.rb to verify version detection logic
- [ ] Update .github/workflows/ci.yml to run 'bundle exec rspec' (or ensure it's already configured)
Add integration tests via GitHub Actions for macOS environments
The ci.yml and style.yml workflows exist but likely don't test actual Homebrew Cask operations on macOS. This tool directly manages user applications, so verifying it works on real macOS environments (not just linting) is high-value. A new contributor can add a macOS-specific workflow that installs a test cask, runs brew-cu commands, and verifies expected behavior.
- [ ] Create .github/workflows/integration-macos.yml with runs-on: macos-latest
- [ ] Add steps to install Homebrew, tap buo/cask-upgrade, and install a test cask (e.g., 'google-chrome' or 'vlc')
- [ ] Add test steps that run: 'brew cu --list' to verify detection, 'brew cu --upgrade' with confirmation, and 'brew cu pin add/remove'
- [ ] Verify that pinned casks are skipped during upgrades via output parsing
- [ ] Add conditional logic to skip if not on macOS or if Homebrew unavailable
Document the plugin architecture and extend module usage in CONTRIBUTING.md
The lib/extend/ directory shows the project extends Cask, Formatter, and Version classes, suggesting a plugin-like architecture. However, CONTRIBUTING.md likely doesn't explain how to add new commands, extend Cask behavior, or add custom formatters. This is a barrier for new contributors wanting to add features like custom upgrade strategies or output formats.
- [ ] Review existing command classes (lib/bcu/command/command.rb base class, all.rb, upgrade.rb) to understand the pattern
- [ ] Review lib/extend/*.rb to document what each monkey-patch adds and why
- [ ] Add 'Architecture' section to CONTRIBUTING.md explaining: command hierarchy, how to add a new command subclass, how to extend Cask/Formatter/Version
- [ ] Add code example: 'To add a new command, create lib/bcu/command/my_command.rb inheriting from Command'
- [ ] Document lib/bcu/module/pin.rb as a utility module and when to create similar modules
- [ ] Add guidance on testing new extensions
🌿Good first issues
- Add integration tests for pin_list.rb, pin_add.rb, and pin_remove.rb commands—currently no test files visible in the repo, so adding test coverage for the pin module would be high-value and low-risk.
- Document the pin storage format and location—the module/pin.rb file lacks inline documentation about where pins are persisted and how the data structure works, making contributions to pin features error-prone.
- Add
--dry-runflag to upgrade.rb—the upgrade command currently executes immediately; adding a dry-run mode would let users preview what would be upgraded without side effects, and the flag parsing infrastructure exists in options.rb.
⭐Top contributors
Click to expand
Top contributors
- @ondrejfuhrer — 70 commits
- @dependabot[bot] — 13 commits
- @Mick235711 — 10 commits
- @Copilot — 1 commits
- @MaximilianPavon — 1 commits
📝Recent commits
Click to expand
Recent commits
c91fc02— Fix private method 'load' error with recent Homebrew (ondrejfuhrer)36b5555— fix trailing whitespace (ondrejfuhrer)c73e81e— Ignore local Claude settings file (ondrejfuhrer)2ce008c— Fix brew cu --help mangling glob asterisks (ondrejfuhrer)9a60bdc— Update examples to use generic pattern instead of cl (ondrejfuhrer)8bcf618— Fix: Glob pattern filtering not displaying filtered cask list (ondrejfuhrer)0916eec— Fix RuboCop style violations (ondrejfuhrer)8b339b8— Fix Style/PartitionInsteadOfDoubleSelect: use partition instead of select/reject (Copilot)c53c9b4— Fix code style violations (ondrejfuhrer)96dac29— Apply suggestion from @ondrejfuhrer (ondrejfuhrer)
🔒Security observations
The homebrew-cask-upgrade project shows reasonable security practices with GitHub Actions configured and dependabot enabled. However, critical information is missing for a complete security assessment: dependency files (Gemfile/gemspec) cannot be analyzed for vulnerabilities, and the actual code implementation in key files is not provided. The main concerns are potential command injection risks common in wrapper tools around system commands, and lack of a formal security disclosure policy. The project would benefit from adding static analysis to CI/CD and a security.md file. Overall security posture appears moderate to good, but cannot be fully evaluated without source code review.
- Medium · Missing dependency file for vulnerability analysis —
brew-cask-upgrade.gemspec / Gemfile (not provided). The brew-cask-upgrade.gemspec file content is not provided, making it impossible to analyze Ruby gem dependencies for known vulnerabilities. This is a Ruby project that likely has external dependencies listed in a Gemfile or gemspec that need security review. Fix: Provide the gemspec and Gemfile contents for dependency vulnerability scanning. Use tools like 'bundle audit' or 'bundler-audit' to check for known vulnerabilities in Ruby dependencies. - Medium · Potential command injection risk in shell execution —
lib/bcu/command/*.rb files (content not provided). The codebase includes command execution patterns (based on file structure suggesting shell command wrapping around Homebrew). Without seeing the actual code in lib/bcu/command files, there's risk of command injection if user input or external data is passed unsanitized to shell commands. Fix: Use safe Ruby methods for command execution (e.g., system with array arguments, Open3.popen3) instead of shell interpolation. Validate and sanitize all user inputs before passing to system commands. Never use backticks or %x{} with unsanitized input. - Low · No explicit security policy or vulnerability disclosure process —
Repository root. The repository does not appear to have a SECURITY.md file or security policy for reporting vulnerabilities responsibly. Fix: Create a SECURITY.md file with instructions for responsible vulnerability disclosure and a contact method for security issues. - Low · Limited visibility into CI/CD security —
.github/workflows/ci.yml, .github/workflows/style.yml. While GitHub Actions workflows are present (.github/workflows/ci.yml, style.yml), the specific content of these workflows is not provided. They may lack security scanning steps. Fix: Ensure CI/CD pipelines include: dependency vulnerability scanning (bundle audit), SAST analysis, and code quality checks. Use dependabot for automated dependency updates (already configured in dependabot.yml).
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.