We have a `HOMEBREW_MACOS_NEWEST_UNSUPPORTED` environment variable
and this is used in `MacOSVersion` to determine prerelease versions
but we don't have a way of easily determining the newest supported
macOS version.
`bump-cask-pr` contains logic that assumes the first key/value in
`MacOSVersion::SYMBOLS` is the newest macOS version but it recently
became clear that this is a prerelease version between WWDC and the
subsequent macOS release. Similarly, `dev-cmd/generate-cask-api.rb`
tries to compute the newest stable macOS version as
`HOMEBREW_MACOS_NEWEST_UNSUPPORTED.to_i - 1` and this will fail
if/when we update that variable to `"26"`, as the macOS version
before 26 is 15, not 25.
This adds a `HOMEBREW_MACOS_NEWEST_SUPPORTED` environment variable,
so we have a straightforward way of quickly identifying the newest
supported macOS version without having to make potentially unreliable
assumptions or do computations to identify the latest non-prerelease
`MacOSVersion` value. This also updates the two aforementioned areas
to use this environment variable to produce the newest stable macOS
version symbol in a more reliable way.
Add a new `brew mcp-server` command for a Model Context Protocol (MCP)
server for Homebrew. This integrates with AI/LLM tools like Claude,
Claude Code and Cursor.
It currently supports the calls needed/used by the MCP Inspector and
Cursor (where I've tested it).
It provides as `tools` the subcommands output by `brew help` but should
be fairly straightforward to add more in future.
It is implemented in a slightly strange way (a standalone Ruby command
called from a shell command) as MCP servers need a faster startup time
than a normal Homebrew Ruby command allows and fail if they don't get
it.
There are a few Ruby libraries available but, given how relatively
simplistic the implementation is, it didn't feel worthwhile to use and
vendor them.
Let's move this from `bin/brew` to make things like e.g. `brew --prefix`
or `brew shellenv` not reset the sudo timestamp.
This is still in a place that ensures that e.g. no untrusted formula or
tap code has been run yet so should have no security implications but
provide mild usability improvements.
Previously, the default temporary directory was /tmp on Linux and
/private/tmp on macOS. On many Linux distros, including at least Fedora,
/tmp is stored in RAM. This diverges from the behavior on macOS and has
led to bugs, most notably the inability to install large bottles on
memory-limited machines.
- if HOMEBREW_TEMP is not writable, use the default temp directory
- when running `brew bundle exec`, strip various temporary directories
from the environment if they are not writable
This uses the logic in `brew.sh` for deciding whether or not to run
`brew update --auto-update` and makes it a dedicated command that can
be used instead of `brew update` in scripts to be really fast in the
no-op case.
`brew update` will always do at least some updating which is a nicer
default but is much slower.
Today if you are in developer mode then `HOMEBREW_NO_SORBET_RUNTIME`
doesn't take effect. But when doing development it's often useful to be
able to disable type-checking, so have that env var take effect even in
developer mode.
- handle reading from variables that may not be defined yet
- avoid unnecessary handling of defined variables
- fix incorrect `env_config.rbi` file entry
- add `HOMEBREW_NO_FORCE_BREW_WRAPPER` to disable
`HOMEBREW_FORCE_BREW_WRAPPER` functionality if needed
Allow the ability for a system administrator to use
`HOMEBREW_BREW_WRAPPER` and `HOMEBREW_FORCE_BREW_WRAPPER` variables to
enforce the usage of a particular `brew` command for non-trivial (e.g.
`brew --prefix` is considered trivial, it doesn't need to write to the
prefix) Homebrew commands.
This also introduces a `HOMEBREW_ORIGINAL_BREW_FILE` variable for some
internal usage; `HOMEBREW_BREW_FILE` was being used internally for
both "how should we shell out to Homebrew" and "what should we use
to check permissions on Homebrew". `HOMEBREW_ORIGINAL_BREW_FILE` is
now used just for the latter case.
Inspired by conversation in
https://github.com/Homebrew/homebrew-bundle/pull/1551 which suggested
this was worth fixing in wider than just `brew bundle`.
The order matters here (in a way that I can't explain, unfortunately).
Example:
bash-3.2$ read -r FOO <nonexistent 2>/dev/null
bash: nonexistent: No such file or directory
bash-3.2$ read -r FOO 2>/dev/null <nonexistent
# no output
FixesHomebrew/discussions#5653.
This provides a >3x speedup for `brew tap` with no arguments (i.e., when
listing taps). It also makes the completion significantly faster.
$ hyperfine --warmup=3 --setup 'git checkout {branch}' --parameter-list branch master,brew-tap-speedup 'brew tap'
Benchmark 1: brew tap (branch = master)
Time (mean ± σ): 1.405 s ± 0.080 s [User: 0.561 s, System: 0.238 s]
Range (min … max): 1.332 s … 1.549 s 10 runs
Benchmark 2: brew tap (branch = brew-tap-speedup)
Time (mean ± σ): 404.1 ms ± 124.8 ms [User: 107.9 ms, System: 200.7 ms]
Range (min … max): 308.8 ms … 693.7 ms 10 runs
Summary
brew tap (branch = brew-tap-speedup) ran
3.48 ± 1.09 times faster than brew tap (branch = master)
On macOS 14 and newer, `/usr/libexec/path_helper` supports setting a
`PATH_HELPER_ROOT` environment variable.
With this set, `path_helper` checks `$PATH_HELPER_ROOT/etc/paths` and
`$PATH_HELPER_ROOT/etc/paths.d` in the same way it checks `/etc/paths`
and `/etc/paths.d`.
We can use this to simplify management of the user's `PATH` variable
when they do `brew shellenv`. In particular, if their system supports
it, we delegate setting the `PATH` environment variable to `path_helper`
instead of our own code. We also write a default `etc/paths` file if one
is not already present.
This is nicer because it simplifies management of the user's `PATH`
variable. For example, if a user wants a keg-only formula to be in their
`PATH`, they can simply add the necessary path to `/etc/paths` or
`/etc/paths.d` without having to do something like `brew link --force`
or adding to `PATH` themselves.
When `/usr/libexec/path_helper` is not available, we just fall back to
the existing code.
- Use a safe fallback in case git rev-parse fails, e.g. if this is not
considered a safe git directory. For hopefully obvious reasons: be
super careful and strict with the inputs we'll accept here.
- Better handle more permission errors when reading or writing to/from
the git describe cache. We don't care about these errors because they
are likely a result of a multiuser configuration where Homebrew is run
as several different users and this is just a (small) performance
improvement.
`GIT_DESCRIBE_CACHE` is a performance optimisation that doesn't clean
up consistently when run with different Homebrew users. Instead of
doing user detection: let's just hide these errors that we don't care
about.
Reported in:
https://github.com/orgs/Homebrew/discussions/5479
`brew -v` was previously equivalent to `brew --version`, but it
currently returns the output of `brew help`. (This also occurs with
`brew -x`, where x is any single character.)
This is because the `-?` pattern matches `-` followed by any single
character. We need to quote it to capture the intended meaning.
This provides a decent speedup:
```
$ hyperfine 'git checkout master; brew help' 'git checkout help_bash; brew help'
Benchmark 1: git checkout master; brew help
Time (mean ± σ): 506.4 ms ± 50.9 ms [User: 223.7 ms, System: 99.9 ms]
Range (min … max): 454.6 ms … 634.1 ms 10 runs
Benchmark 2: git checkout help_bash; brew help
Time (mean ± σ): 109.5 ms ± 57.1 ms [User: 1
```
and compares favourably to `pip3 help`:
```
$ hyperfine 'brew help' 'pip3 help'
Benchmark 1: brew help
Time (mean ± σ): 72.9 ms ± 15.9 ms [User: 4.9 ms, System: 6.3 ms]
Range (min … max): 53.6 ms … 126.6 ms 31 runs
Benchmark 2: pip3 help
Time (mean ± σ): 171.5 ms ± 6.1 ms [User: 131.6 ms, System: 24.7 ms]
Range (min … max): 164.2 ms … 189.3 ms 15 runs
Summary
brew help ran
2.35 ± 0.52 times faster than pip3 help
```
Similarly to have we have with other commands, use Bash to speed it up.
Before:
```
$ hyperfine "brew list"
Benchmark 1: brew list
Time (mean ± σ): 559.9 ms ± 122.8 ms [User: 176.2 ms, System: 126.2 ms]
Range (min … max): 503.2 ms … 907.3 ms 10 runs
```
After:
```
$ hyperfine "brew list"
Benchmark 1: brew list
Time (mean ± σ): 223.7 ms ± 31.9 ms [User: 35.0 ms, System: 53.4 ms]
Range (min … max): 198.1 ms … 302.4 ms 10
```
Petty after comparison because someone on Homebrew/discussions compared
them:
```
$ hyperfine "brew list" "pip3 list"
Benchmark 1: brew list
Time (mean ± σ): 213.1 ms ± 22.8 ms [User: 34.2 ms, System: 49.2 ms]
Range (min … max): 191.2 ms … 272.3 ms 13 runs
Benchmark 2: pip3 list
Time (mean ± σ): 271.7 ms ± 4.6 ms [User: 191.9 ms, System: 40.0 ms]
Range (min … max): 264.7 ms … 281.4 ms 10 runs
Summary
brew list ran
1.28 ± 0.14 times faster than pip3 list
```
---
While we're here, also add the `HOMEBREW_CASKROOM` environment variable
to make things a bit cleaner and use `--caskroom` in documentation.
Co-authored-by: Carlo Cabrera <30379873+carlocab@users.noreply.github.com>