Production-grade automated Homebrew and NPM package management for macOS only. Runs 3x daily via launchd with intelligent pre-flight checks, differential logging, and graceful error handling.
β οΈ macOS Only: This tool uses macOS-specific features (launchd, plist files) and will not work on Linux or Windows.
- Features
- Installation
- Configuration
- Usage
- Management Commands
- Troubleshooting
- Requirements
- Development
- Documentation
- Contributing
- License
β Smart Pre-flight Checks
- Network connectivity verification
- Disk space validation (minimum 5GB)
- Homebrew installation check
β Comprehensive Updates
- Update Homebrew itself
- Upgrade all formulae
- Upgrade all casks (with --greedy flag)
- Cleanup old versions (30-day retention)
- Autoremove unused dependencies
β NPM Integration
- Checks for global npm installation
- Updates all global npm packages
- Skips gracefully if npm is missing
β Robust Error Handling
- Lock file prevents concurrent runs
- Automatic stale lock removal (>2 hours)
- Process cleanup on exit/interrupt
- Graceful failure handling
β Intelligent Logging
- Differential logging (only logs changes)
- Automatic log rotation at 10MB
- 24-hour log retention
- Timestamped entries
- Separate error log
β System Integration
- Runs 3x daily (9 AM, 3 PM, 9 PM)
- Low priority I/O and CPU
- Desktop notifications on completion
- Health checks and summaries
This tool uses launchd (macOS's native task scheduler) to run a compiled Rust binary on schedule.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β macOS launchd β
β (reads ~/Library/LaunchAgents/com.USER.brew-update.plist) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ Executes at 9AM, 3PM, 9PM
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ~/Scripts/brew-update β
β (Native ARM64/x86 Mach-O binary) β
β Compiled from Rust source β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββΌββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββ ββββββββββββββββ βββββββββββββ
β Pre-flightβ β brew update β β Logs β
β Checks β β brew upgrade β β & Notify β
ββββββββββββ ββββββββββββββββ βββββββββββββ
macOS deprecated cron in favor of launchd, which offers:
- Power-aware scheduling β skips runs when on battery if configured
- Missed run recovery β runs immediately after wake if a schedule was missed
- Better process management β proper signals, resource limits, sandboxing
- Native integration β works with macOS login/logout seamlessly
The plist file tells launchd when and how to run the binary:
<key>ProgramArguments</key>
<array>
<string>/Users/YOUR_USERNAME/Scripts/brew-update</string>
</array>
<key>StartCalendarInterval</key>
<array>
<dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
<dict><key>Hour</key><integer>15</integer><key>Minute</key><integer>0</integer></dict>
<dict><key>Hour</key><integer>21</integer><key>Minute</key><integer>0</integer></dict>
</array>The binary is not a script β it's compiled native machine code (Mach-O ARM64 on Apple Silicon, x86_64 on Intel), making it fast and dependency-free at runtime.
# Clone the repository
git clone https://github.com/organicnz/brew-auto-update.git
cd brew-auto-update
# Run the installer
cargo run --release --bin installThe installer automatically:
- Detects your username
- Detects Homebrew installation path
- Creates necessary directories
- Installs and configures everything
- Runs an initial test
- Install and Build:
cargo run --release --bin install
# This will compile the Rust binary and install it to ~/Scripts/brew-update- Install the launchd plist:
# Replace USER with your username
sed "s/USER/$(whoami)/g" com.organic.brew-update.plist > ~/Library/LaunchAgents/com.$(whoami).brew-update.plist
# Load the agent
launchctl load ~/Library/LaunchAgents/com.$(whoami).brew-update.plist- Verify installation:
launchctl list | grep brew-updateCustomize installation by setting these before running ./install.sh:
# Schedule (default: 9 AM, 3 PM, 9 PM)
export BREW_UPDATE_HOUR1=8
export BREW_UPDATE_MINUTE1=0
export BREW_UPDATE_HOUR2=14
export BREW_UPDATE_MINUTE2=30
export BREW_UPDATE_HOUR3=20
export BREW_UPDATE_MINUTE3=0
# System settings
export BREW_UPDATE_NICE_LEVEL=10 # CPU priority (0-20, higher = lower priority)
export BREW_UPDATE_THROTTLE_INTERVAL=300 # Min seconds between runs
export BREW_UPDATE_EXIT_TIMEOUT=7200 # Max runtime (2 hours)
export BREW_UPDATE_LOG_RETENTION_DAYS=1 # Keep logs for 24 hours
export BREW_UPDATE_MIN_DISK_SPACE_GB=5 # Minimum free space required
# Then install
cargo run --release --bin installThese can be set in the script or plist environment:
export LOG_DIR="$HOME/Library/Logs" # Log directory
export LOG_RETENTION_DAYS=1 # Keep logs for 24 hours
export MIN_DISK_SPACE_GB=5 # Minimum free space required
export LOCK_TIMEOUT=7200 # Max runtime (2 hours)
export MAX_LOG_SIZE=10485760 # Log rotation size (10MB)Edit the plist file to change run times. Default schedule:
- 9:00 AM
- 3:00 PM
- 9:00 PM
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<!-- Add more time slots as needed -->
</array>Once installed, the script runs automatically on schedule. No action needed!
~/Scripts/brew-update# Main log
tail -f ~/Library/Logs/brew-updates.log
# Error log
tail -f ~/Library/Logs/brew-updates-error.log
# Launchd output
tail -f ~/Library/Logs/brew-update-stdout.log# Check status
launchctl list | grep brew-update
# Disable automatic runs
launchctl unload ~/Library/LaunchAgents/com.$(whoami).brew-update.plist
# Enable automatic runs
launchctl load ~/Library/LaunchAgents/com.$(whoami).brew-update.plist
# Trigger immediate run
launchctl start com.$(whoami).brew-update
# View next scheduled run
launchctl print gui/$(id -u)/com.$(whoami).brew-update | grep next# Check if loaded
launchctl list | grep brew-update
# Check for errors
tail ~/Library/Logs/brew-update-stderr.log
# Verify binary permissions
ls -la ~/Scripts/brew-update# Remove manually (script auto-removes stale locks >2 hours)
rm -f /tmp/brew-update.lockThe script will skip updates and notify you if no network is available. This is normal behavior.
# Unload the agent
launchctl unload ~/Library/LaunchAgents/com.$(whoami).brew-update.plist
# Remove files
rm ~/Library/LaunchAgents/com.$(whoami).brew-update.plist
rm ~/Scripts/brew-update
rm -rf ~/Library/Logs/brew-update*- macOS 10.14 (Mojave) or later (required)
- Homebrew installed (install here)
- Rust/Cargo installed (for building)
- Write access to
~/Library/Logs
Note: This tool is designed exclusively for macOS and uses launchd for scheduling. It will not work on Linux or Windows systems.
- Runs as user (not root)
- No sudo required
- Sandboxed to user environment
- Safe PATH configuration
# Clone the repo
git clone https://github.com/organicnz/brew-auto-update.git
cd brew-auto-update
# Setup development tools (lefthook, rustfmt, clippy)
cargo run --bin setupLefthook runs automatically on commit:
- ShellCheck linting
- Bash syntax validation
- Plist template validation
- Secrets detection
- Markdown linting
Run manually:
lefthook run pre-commit- π Configuration Guide - Detailed configuration options
- π€ Contributing Guidelines - How to contribute
- π System Documentation - Technical details
- π License - MIT License
Contributions welcome! Please see Contributing Guidelines for details.
MIT License - see LICENSE for details
Created for automated Homebrew maintenance on macOS systems.
- Initial release
- Network connectivity check
- Disk space validation
- Differential logging
- Automatic log rotation
- 3x daily scheduling
- Desktop notifications
- Added global NPM package auto-updates
- Rewrite in Rust for safety and performance
- Native binary instead of Bash script