- `brew bump` reported formulae as up to date when a newer upstream
release was suppressed by the release cooldown, confusing upstream
authors waiting for autobump.
- Show the suppressed version with how recently it was released, add
a `Bump-ready version:` line for what can be bumped now and change
the headline from `is up to date!` to `has a new version in
release cooldown`.
- Fixes#23396.
- Drop the status write that fork pull requests cannot receive.
- Pin the `2026.08.03.2` action release that fails validation jobs.
- Report the required check for synthetic merge queue commits.
- After #23381, homebrew-core CI fails installing dependencies on
version-bump PRs: the bumped formula's stale bottle block derives
its manifest URL from the new version, whose bottle has not been
published yet, and `DownloadQueue#fetch` treats every bottle
manifest failure as fatal.
- Before that change the default ask-mode plan fetched manifests
synchronously via `Formula#fetch_bottle_tab`, which rescues
download errors so dependency resolution falls back to a full
install, and the installer's memoisation then kept the queue from
retrying the download.
- Add `DownloadQueue#fetch(allow_failures:)`: failed downloads are
still reported but neither raise nor mark the fetch or run as
failed. Use it for the metadata-only drains (`fetch_formulae`'s
bottle manifest waits, `brew install`'s ask-mode drain and
`brew upgrade`'s tab prefetch), restoring the synchronous path's
tolerance. Manifest failures in fetches that pour bottles remain
fatal.
`brew info --installed --json=v2` emits `==> Downloading Homebrew API
data` to stdout when the API data needs (re)downloading, corrupting
the JSON for anything parsing it (e.g. piping to `jq`).
The heading was added in 284cd0ed4c (#23381) as an unconditional
`oh1`, regressing the intent of 43137fe877 (#20980), which moved the
queue's non-TTY per-download report lines to stderr for exactly this
reason. Print the heading to stderr when stdout is not a TTY so it
stays off parsed stdout and on the same stream as those report lines.
TTY behaviour is unchanged.
Use `$stderr.puts oh1_title(...)` rather than `$stderr.oh1` as
RSpec's `to_stderr` matcher replaces `$stderr` with a plain
`StringIO` that lacks the `Utils::Output::Mixin` extension.
- `brew install` only started network transfers after preinstall
checks and, in ask mode, fetched each bottle manifest serially
while computing the dry-run plan.
- Enqueue bottle manifests on the shared download queue right after
building formula installers in both ask and no-ask modes so
transfers overlap preinstall checks and dependant scanning and
manifests for multiple formulae download concurrently. Ask mode
drains them under a `Downloading bottle manifests` heading before
printing the plan; warm runs enqueue nothing and stay silent.
- Once downloads are confirmed (`--yes` installs, reinstall, upgrade
and other `Install.fetch_formulae` callers), also enqueue the
formula's own bottle in `FormulaInstaller#prelude_fetch`: the blob
URL needs neither the manifest nor dependency resolution, so both
transfer concurrently and staging joins the same queue cycle.
- Skip the `enqueue_fetch` requeue for bottles the prelude fetch
already enqueued so a completed early download is not reported a
second time.
- Give `DownloadQueue#fetch` `only:` and `heading:` so a heading is
always printed before any queue output and never for empty fetches:
dependency resolution inside `Install.fetch_formulae` waits on just
the bottle manifests it needs (under `Downloading bottle manifests`)
and the cask source pre-fetch on just its cask files, keeping other
in-flight downloads queued and unreported so bottles only ever
appear under the `Fetching downloads for:` heading, which now
prints lazily from the fetch that reports them (`brew upgrade`
enqueues before it knows that heading's contents). This replaces
`Install.show_combined_fetch_downloads_heading`, `brew upgrade`'s
manual manifest heading predicate and its dead
`show_downloads_heading` plumbing.
- Instrument `Install.perform_preinstall_checks_once` as a
`preinstall_checks` phase timing to keep the reordering visible.
- Archive-cold `brew install hello`: the first transfer starts at
~375ms instead of ~442ms, the bottle no longer waits for the
manifest round trip (~672ms before, ~375ms now) and `--yes` wall
time drops around 30%. Two-formula ask-mode installs drop one full
manifest round trip (~1240ms to ~1030ms).
Occasionally PR's or issues are opened in homebrew/cask due to
applications not matching the right case name on case-sensitive
filesystems. These are relatively rare since most users run with the
standard case-insensitive, but it's still a negative user experience.
This audit is intended to catch these cases.
We will be degrading support to Tier 3 next month so avoid creating new
taps that use Intel macOS. Users that specifically want it should
manually update the generated workflow instead which makes sure they are
aware that support will likely end next year.
- This `formula` helper returning `Class.new(::Formula, &block)` is not
of _instance_ type `Formula` (set in 540457b88b), but
`T.class_of(Formula)`.
- But the accurate `T.proc.bind(T.class_of(::Formula))` can't be used
instead because Sorbet errors with: "Malformed `bind`: Can only bind
to simple class names". See
https://github.com/Homebrew/brew/pull/23318#issuecomment-5087573557.
- Uses of this test helper that need to be typechecked still need
`T.bind(self, T.class_of(Formula))` inline until Sorbet can bind to
`T.class_of` of a class with a fixed `type_template` (maybe never).
- Ruby 4.0's `continuation` warns `callcc is obsolete; use Fiber
instead` whenever it is required, which happens on every `brew`
command that loads a formula from source.
- Run `Ignorable.hook_raise` blocks in a `Fiber`: `raise` now pauses
at the raise site and asks an `on_ignorable` callback whether to
resume (`:ignore`) or raise there as usual, replacing the rescue
plus continuation jump and `Ignorable::ExceptionMixin#ignore`.
- `Debrew` menus and `Formulary` `ignore_errors` decisions now happen
before the stack unwinds, so `ensure` blocks only run when an
exception is actually raised.
- Only require `ignorable` when `Formulary` uses `ignore_errors` and
drop the obsolete `brew verify-undefined` `Warnings` guard.
Fixes https://github.com/Homebrew/brew/issues/23384
brew services works on Linux but the caveat was firing because it
interpreted the nonexistence of /usr/bin/pbpaste as failure. Now the
check is identical to that in the services subcommand itself.
- `pycall` was Homebrew's only in-process Python embedding: a native
gem that dlopens `libpython`, needed the `__PYVENV_LAUNCHER__` hack
for macOS framework Pythons, hand-written Sorbet stubs and
tapioca/RuboCop exclusions.
- A pure Ruby HTTP port was rejected: InfluxDB Cloud Serverless only
serves SQL over Arrow Flight gRPC (it has no `/api/v3/query_sql`
endpoint) and the analytics schema keeps `package`, `tap_name` and
`options` as fields, which InfluxQL cannot `GROUP BY`; Flux is
deprecated on InfluxDB 3. Ruby Flight clients need heavier native
dependencies than `pycall` itself.
- Moving the whole query operation into Python was rejected: the JSON
output depends on `Homebrew::EnvConfig` defaults, `MacOSVersion`
pretty names and WSL suffix handling that would drift if duplicated
outside Ruby.
- `influxdb-query.py` runs from a `uv`-managed virtualenv with the
same pinned `influxdb3-python` and speaks a documented protocol: a
JSON request on stdin, JSON Lines rows on stdout and the token read
from `HOMEBREW_INFLUXDB_TOKEN` so credentials stay off the command
line.
- Arrow record batches now stream via `mode="reader"` instead of
being copied record-by-record across PyCall's FFI bridge.
- Python dependencies moved from `pip-compile`d `requirements.txt` to
`pyproject.toml` and a hash-verified `uv.lock` installed with
`uv sync --frozen`: `uv` is a single dependency-free bottle
installed 2.5x as often as versioned Python formulae, replaces the
five bottles `python@3.13` needed and cold-installs the whole
virtualenv faster than `pip` installed the packages alone, so
`actions/setup-python` and dependabot's `pip` ecosystem are
replaced by `astral-sh/setup-uv` and the `uv` ecosystem.
- `uv` also provisions the interpreter: `.python-version` pins 3.14,
the newest Python in Homebrew, and `requires-python = ">=3.13"`
keeps future bumps to a one-line `.python-version` change.
- `brew formula-analytics --setup` still prepares everything for
offline runs and verifies the bridge import via the script's
`--check` flag.
- `brew verify-undefined` also guards `InfluxDBClient3` and `PyCall`
after requiring `dev-cmd/formula-analytics`.
- The TSM-era `transform_analytics_to_counts.json` Flux task was
referenced by nothing so is removed.
The Documentation workflow installs Vale unpinned, and 3.17.0 reports two
errors that 3.16.0 did not, so every pull request built after its release
fails the docs job on unchanged content. Both are real violations of our
own styles rather than new false positives.
Drop an Oxford comma in the conference reimbursement list, and link to
How-To-Open-a-Homebrew-Pull-Request.md by the name Homebrew.Terms exempts,
which is also how every other page refers to it.
Services can now read environment variable overrides from
$HOMEBREW_USER_CONFIG_HOME/services/<formula>.env (defaults to
~/.homebrew/services/<formula>.env). The file uses KEY=value
format, one per line, with # comments. User overrides take
precedence over formula-defined variables and are merged into
the generated plist/systemd unit on every start/restart.
This gives users a persistent way to customise service
environment variables that survives package upgrades.
Disabled for root services to avoid TOCTOU risks with
symlink-swapping and caller-influenced HOME/XDG state.
Non-root services validate that the override file is not
world-writable or group-writable, and warn on unparseable
lines.
Feature is documented in the "brew services --help" output.
- One malformed formula or cask (e.g. a bottle `root_url` that is an
invalid URI) raised while enqueueing downloads and aborted the whole
`brew upgrade` batch with an internal stack trace.
- Rescue anything raised by an individual formula's or cask's prelude
and enqueue steps so the offending package is reported and skipped,
the rest still proceed and the command exits nonzero.
Fixes https://github.com/Homebrew/brew/issues/23377.
- Stop documenting the temporary implicit `var` base for formula steps.
- Update examples to specify their intended path base.
- Record the official-tap migration, RuboCop enforcement and runtime
default removal as separate follow-up changes.
- Every command loading internal API data paid a full `JSON.parse` of
the 13MB packages payload (~8500 formulae and ~7700 casks, ~80ms and
a large retained object graph) even when installing one formula.
- Add `Homebrew::API::PackagesIndex`, a byte-offset index sidecar over
the signature-verified payload. The index is derived, unverified
cache data guarded in layers: the payload bytes it points into are
RSA-PSS-verified every run, loading requires the recorded top-level
spans to tile that payload exactly (proving the formulae and casks
section spans are the real top-level values) and every lookup
revalidates that its offsets sit at the expected `"<name>":` key
inside the requested section's span and that the slice parses. A
forged or stale index therefore cannot inject unverified content or
remap a name to another entry, even a same-named key in the other
section; it fails validation and callers fall back to a full parse.
- Build the index whenever a download, revalidation or index miss has
already paid for a full parse, by locating each entry's bytes via
their exact `JSON.generate` round trip (the payload is generated by
the same serialiser, verified against the whole document up front).
- `Internal` now serves per-name entries (`formula_hash`, `cask_hash`,
name lists and membership) and the small top-level keys through the
index, materialising `formula_hashes`/`cask_hashes` only for
full-iteration callers such as `search` and `formula_auditor`.
- `write_names_file!`, `write_aliases_file!` and
`write_executables_file!` take blocks so their freshness short
circuits no longer materialise every entry on warm loads.
- `brew update` prewarms the index: `brew update-report`'s existing
`write_names_and_aliases` call rebuilds the payload sidecar and
index whenever the envelope changed, during the full parse it needs
for the names files anyway, so the first command after an update
loads through the index. The previous-OS-version removals in
`cmd/update.sh` and `brew cleanup --scrub` keep the current OS's
`.payload.index` alongside its `.payload` instead of deleting it on
every update.
- Internal API data load drops from ~100ms to ~30ms in `brew install`
(signature verification retained) and a warm no-op `brew install`
from ~530ms to ~440ms.