Skip to main content

mac-upkeep

PyPI CI Python License macOS

Automated macOS maintenance CLI. Runs Homebrew updates, dev tool cache cleanup (gcloud, pnpm, uv), Fish plugin updates, system optimization, and Brewfile enforcement on boot + weekly via brew services — zero config required.

mac-upkeep demo

Install

brew install calvindotsg/tap/mac-upkeep
brew services start mac-upkeep  # runs on boot + Monday 12 PM

Or via uv:

uv tool install mac-upkeep   # persistent install
uvx mac-upkeep run            # one-off without installing

Tasks

Task Description Schedule
brew_update Update Homebrew package database Weekly
brew_upgrade Upgrade outdated formulae and casks Weekly
gcloud Update Google Cloud SDK components Monthly
pnpm Prune pnpm content-addressable store Monthly
uv Prune uv package cache Monthly
fisher Update Fish shell plugins Weekly
mo_clean Clean system and user caches (Mole) Weekly
mo_optimize Optimize DNS, Spotlight, fonts, Dock (Mole) Weekly
mo_purge Remove old project artifacts (Mole) Monthly
brew_cleanup Remove old versions and cache files Monthly
brew_bundle Remove packages not in Brewfile Weekly
git_sync Pull configured git repositories Daily

Tasks auto-detect installed tools — missing tools are skipped. Use --force <task> to run a specific task on demand.

mac-upkeep tasks  # See all tasks with status, frequency, and next run

Usage

mac-upkeep run                       # Run tasks (frequency-checked)
mac-upkeep run --dry-run             # Preview without executing
mac-upkeep run --force brew_update   # Run only brew_update
mac-upkeep run --force all           # Run all, ignoring schedule
mac-upkeep run --debug               # Verbose output
mac-upkeep tasks                     # List tasks with status and next run
mac-upkeep init                      # Generate config (detects your tools)
mac-upkeep show-config --default     # Show all available task options
mac-upkeep show-config               # Show your config overrides
mac-upkeep setup                     # Print sudoers rules
mac-upkeep status                    # Show scheduling dashboard
mac-upkeep logs                      # View last 20 log lines
mac-upkeep logs -f                   # Follow logs
mac-upkeep --version                 # Show version

Configuration

Works out of the box with zero configuration. To customize, generate a starter config:

mac-upkeep init

This probes your system, detects installed tools, and writes a commented config to ~/.config/mac-upkeep/config.toml. Only detected tasks are listed. Built-in defaults apply automatically — uncomment lines to override.

To see all available tasks and options:

mac-upkeep show-config --default

Override examples

# ~/.config/mac-upkeep/config.toml

# Disable a task
[tasks.gcloud]
enabled = false

# Change frequency (daily, weekly, or monthly)
[tasks.brew_update]
frequency = "monthly"

# Set Brewfile path explicitly
[paths]
brewfile = "~/.config/Brewfile"

Custom tasks

Add your own tasks using the same format:

[tasks.docker_prune]
description = "Prune Docker system"
command = "docker system prune -f"
detect = "docker"
frequency = "monthly"

# Control execution order
[run]
order = ["brew_update", "brew_upgrade", "docker_prune", "brew_cleanup", "brew_bundle"]

git_sync

Pull configured git repositories daily with git pull --ff-only. Opt-in — list your repos explicitly:

[git_sync]
repos = [
    "~/code/my-project",
    "~/work/max-*",       # glob patterns supported
]
skip_dirty = true         # skip repos with uncommitted changes

Each repo is skipped with a reason if it's not a git repo, has no remote, has no upstream branch, or (when skip_dirty = true) has uncommitted changes.

Authentication

Any of the following work under launchd without mac-upkeep-side configuration:

  • SSH + IdentityAgent (recommended under launchd): a path-based entry in ~/.ssh/config pointing at any SSH agent's UNIX socket. Works because the directive is a file path, not the SSH_AUTH_SOCK env var that launchd would strip.
  • HTTPS + credential helper: gh auth setup-git or git config --global credential.helper osxkeychain. Requires the helper binary on the launchd PATH.
  • [url].insteadOf rewrite: force SSH regardless of remote protocol by rewriting https://<host>/ in ~/.gitconfig to a matching SSH Host alias. Bypasses HTTPS auth entirely.

git_sync sets GIT_TERMINAL_PROMPT=0 and a no-op GIT_ASKPASS default (user-set GIT_ASKPASS is respected) so misconfigured auth fails in milliseconds instead of stalling to the 60 s subprocess timeout.

Environment variables

MAC_UPKEEP_GCLOUD=false mac-upkeep run              # Disable a task
MAC_UPKEEP_GCLOUD_FREQUENCY=monthly mac-upkeep run  # Override frequency

Sudoers

mo_clean and mo_optimize require passwordless sudo for the mo binary:

mac-upkeep setup > /tmp/mac-upkeep.sudoers
sudo visudo -cf /tmp/mac-upkeep.sudoers    # must print "parsed OK" before installing
sudo install -m 0440 -o root -g wheel /tmp/mac-upkeep.sudoers /etc/sudoers.d/mac-upkeep

Validate before installing — a malformed file in /etc/sudoers.d/ can lock you out of sudo.

⚠ Upgrading from < 3.0.0 — action required

The sudoers file is installed manually, so brew upgrade does not update it. Reinstall it using the commands above.

Releases before 3.0.0 generated env_keep += "HOME". Sudo preserves HOME but still resets USER to root, and mole compares $HOME's owner against $USER to decide whether your home directory needs a permissions repair. The mismatch makes it run diskutil resetUserPermissions / $(id -u) — and under sudo id -u is 0, so it attempts to reset your home directory to root's uid. The call fails, which is the only reason this is noisy rather than destructive, but it also makes mo optimize exit non-zero on every run.

3.0.0 generates env_keep += "HOME USER LOGNAME". Verify with sudo -n $(brew --prefix)/bin/mo optimize </dev/null >/dev/null; echo $? — it should print 0.

Contributing

See CONTRIBUTING.md for development setup and conventions.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mac_upkeep-3.0.1.tar.gz (317.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mac_upkeep-3.0.1-py3-none-any.whl (31.3 kB view details)

Uploaded Python 3

File details

Details for the file mac_upkeep-3.0.1.tar.gz.

File metadata

  • Download URL: mac_upkeep-3.0.1.tar.gz
  • Upload date:
  • Size: 317.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mac_upkeep-3.0.1.tar.gz
Algorithm Hash digest
SHA256 efdb0c8c8d0892b69d3f4f900ac378acd763b9d77e1893addef31320afa62dc3
MD5 04dcda6903a2f977f992949a5c748c9f
BLAKE2b-256 6aa8cd1b1de983998aaa4c1b7a20dd96fb90e950016d30ce77f31e862648ce45

See more details on using hashes here.

Provenance

The following attestation bundles were made for mac_upkeep-3.0.1.tar.gz:

Publisher: release.yml on calvindotsg/mac-upkeep

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mac_upkeep-3.0.1-py3-none-any.whl.

File metadata

  • Download URL: mac_upkeep-3.0.1-py3-none-any.whl
  • Upload date:
  • Size: 31.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mac_upkeep-3.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 241ccf6687973279533a6eccbe1ddcab28bc609a20f038ee3a2eeea3f6fc4526
MD5 1a91cd5c3ebf8aa7195f9d25822d2cf7
BLAKE2b-256 88bc93cb0d09b36bbf257349d1145aca135946e38e8e2462460f7e19b12b1848

See more details on using hashes here.

Provenance

The following attestation bundles were made for mac_upkeep-3.0.1-py3-none-any.whl:

Publisher: release.yml on calvindotsg/mac-upkeep

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page