- Ignore intentional bootstrap constant reassignments, RBI declarations
and OS-specific method overrides.
- Allow valid names unavailable to the project-only index.
- Retain absolute core constants where inherited lookup can make the
indexed autocorrection unsafe.
- Run each complete cask step block in one isolated subprocess and all
generated completions in another phase-scoped sandbox.
- Share sandbox selection, fork fallback, install-hook rules and child
error reporting with formula build, post-install and test processes.
- Restrict home, network and filesystem access while preserving `brew`
and supporting explicit command write paths.
- Keep JSON payloads compact and independent of cask Ruby files.
- Remove the completed official-tap migration plan.
Formula install-step paths now serialise only a base that was explicitly
specified. RuboCop prevents relative official-tap paths from relying on the
current working directory.
Run structured-only API post-installs from the current JSON data so old
formula snapshots embedded in bottles cannot restore the removed default.
Keep using bottle snapshots for formulae that still have Ruby hooks.
- Reject relative formula step paths without an explicit base.
- Autocorrect paths that previously relied on the temporary `var`
compatibility default.
- Keep absolute paths and install-time path tokens unchanged.
- Align casks with formulae: platform support comes from
`depends_on :macos`/`:linux`/`macos:` data rather than generation
heuristics guessing intent from `os` stanzas, Linux checksums or
`on_linux` blocks.
- `Cask#to_hash_with_variations` now emits variations for every
valid OS/arch tag whenever `on_system` blocks exist, matching
`Formula#to_hash_with_variations`; the Linux-specific gate and its
`sha256_set_for_linux?` and `on_linux_blocks_exist?` tracking are
removed. macOS-only casks publish truthful Linux variations, e.g.
a `null` `sha256`, instead of omitting them.
- `Cask::Installer` gains a first-class unsupported-system error:
API-loaded casks with no activatable artifact for the running
system fail with "This cask is not available on macOS/Linux."
instead of installing nothing. Audited casks always declare an
activatable artifact for the systems they support, so missing
artifacts in API data mean the system is unsupported. Source
loads keep working for unaudited casks, e.g. naked containers.
- A sweep of the full homebrew/cask generation pipeline (all casks,
both Linux tags, including internal per-tag payloads) confirmed
no cask needs new `depends_on` annotations and nothing regresses.
- `Cask#to_hash_with_variations` skipped all Linux variations for
casks declaring Linux support only inside `on_linux`/`on_system`
blocks, e.g. `zen`, as the API JSON is generated on macOS where
those blocks are invisible; the published JSON then fell back to
macOS artifacts on Linux and installs failed with a misleading
"This cask requires macOS." error. Track `on_linux`/`on_system`
blocks and emit Linux variations for casks that have them.
- `sha256` raised for an architecture missing a checksum for the
running OS, e.g. `unity-hub` on `arm64_linux`, so its variation
was silently dropped and ARM64 Linux fell back to macOS data too.
Raise only on the real system: under `SimulateSystem` simulation
`sha256` is now nil so variations for missing architectures carry
their `on_linux` `depends_on arch:` and artifacts and Linux users
get the correct unsupported-architecture error instead.
- `depends_on macos:` inside an `on_arm`/`on_intel` block did not
mark a cask macOS-only, but arch blocks are evaluated on every OS
so the dependency applies there too. Casks like `qlc+` that only
declare macOS inside arch blocks claimed to support Linux while
carrying a macOS requirement. Only OS blocks now scope a
dependency to one OS; the checks for combining macOS `depends_on`
forms still treat every `on_system` block alike.
- `Readall.valid_casks?` skipped casks whose files textually matched
`depends_on macos:` anywhere, including inside `on_macos` blocks,
so cross-OS casks like `unity-hub` were never validated for Linux.
Rely on `supports_linux?` instead and let `depends_on arch:` excuse
architectures a cask deliberately omits Linux checksums for.
- Add `appimagedir` to the test cask config so `app_image` artifacts
can be serialised in tests.
Fixes https://github.com/Homebrew/brew/issues/23427.
- `brew readall`: validate tap formulae and casks across forked
workers (combination loop inside each worker so the `on_system`
cache keeps its per-file locality) and syntax-check Ruby files
in-process with `RubyVM::InstructionSequence.compile_file` and a
`Warning` buffer instead of spawning `ruby -c -w` per file.
- `brew style`: run shellcheck+shfmt and actionlint on background
threads with buffered output while RuboCop runs on the main
thread, chunk shellcheck across CPU cores and pass `--parallel`
to RuboCop with `--fix` (supported since RuboCop 1.41).
- Resolve linter executables before spawning threads so they
cannot race to install formulae.
- `Readall.valid_aliases?`: use a single glob and `Set` lookup
rather than one glob per alias.
- Fix an `end` indentation warning in `cask/cask.rb` that made
`brew readall --syntax` fail.
- Hyperfine benchmarks (18-core Mac, mean of 2 runs, warm RuboCop
cache for style runs, all exit codes 0):
- `brew readall homebrew/core`: 34.36s -> 4.71s (7.3x faster)
- `brew readall homebrew/cask`: 40.69s -> 5.17s (7.9x faster)
- `brew style homebrew/core` (warm): 2.93s -> 2.20s
- `brew style homebrew/cask` (warm): 2.37s -> 2.18s
- `brew style` on Homebrew/brew itself: 13.2s -> 7.8s
- `brew readall --syntax`: 18.2s -> 0.5s
- Tap style runs are RuboCop-bound so barely change; the larger
style win is on Homebrew/brew where all four linters run. CI
runners with fewer cores should expect roughly 3-4x on readall.
- Since the switch to `MachO.codesign!` in ruby-macho 6.0, Intel macOS
rejects the ad-hoc signatures on larger relocated binaries (e.g.
`libruby`, the Python framework) and kills hardened-runtime programs
such as MacVim's `vim` at launch with `CODESIGNING, Invalid Page`:
https://github.com/Homebrew/brew/issues/23418
- The signatures have correct page hashes and pass `codesign --verify`
on newer macOS, so this looks like an Intel macOS verifier quirk
rather than simple corruption.
- Restore the pre-6.0 behaviour on Intel: leave unsigned binaries
unsigned and use `codesign` to re-sign only the binaries whose
existing signature our modifications have just broken, e.g. MacVim's
Xcode-ad-hoc-signed hardened-runtime `vim`.
- Restore the fatal preinstall developer tools check, now skipped on
Apple Silicon rather than gated to it, as `codesign` requires the
Command Line Tools while ruby-macho does not.
- Keep `MachO.codesign!` on Apple Silicon, where it is required,
proven and avoids a `codesign` subprocess per relocated file.
- A plain `brew upgrade` raised "It seems there is already a
Binary at ..." even when the existing symlink resolved to the
exact source about to be linked, because the realpath check sat
behind `force`/`adopt`. The revert left the symlink in place so
every retry failed identically until manual intervention.
- Treat an already-correct symlink as a no-op: log and skip the
link. Genuine conflicts, such as a real file at the target or a
symlink elsewhere, still require `--force` or `--adopt`.
- Rescue errors resolving the target, like `conflicting_formula`
does, so unreadable symlinks fall back to the existing conflict
and error handling instead of raising `Errno` exceptions.
- FixesHomebrew/brew#23426.
Once homebrew/cask is migrated, the style cops can enforce the intended
order for platform blocks and generated artifact DSLs.
- register platform blocks, system variables and generated artifacts
- keep system variables after versions so interpolation remains valid
- remove the temporary completion-grouping migration allowance
- update affected fixtures, documentation and regression coverage
- `Homebrew.default_download_queue` memoizes its queue on the `Homebrew`
module, so an example stubbing `Homebrew::DownloadQueue.new` at first
use leaked an RSpec double into later examples and the `at_exit`
shutdown hook, randomly crashing test runs with
`RSpec::Mocks::OutsideOfExampleError` after every example passed.
- Shut down and drop the memoized queue after every example instead.
A leaked double is only dropped as it cannot receive `shutdown`
outside the per-example rspec-mocks lifecycle.
- Add ordered regression specs covering the leak and the reset.
- Landlock needs no separate executable, installation or `sysctl`
configuration, so use it as the only Linux sandbox implementation
rather than an opt-in behind `$HOMEBREW_SANDBOX_LINUX_LANDLOCK`.
- Delete `Sandbox::Bubblewrap`, the `brew setup-sandbox` command and
the implicit `bubblewrap` dependency, none of which Landlock needs.
- Remove the Bubblewrap-era `Sandbox` API (`ensure_sandbox_installed!`,
`configure!`, `configuration_commands`, `sandbox_install_command`)
and its call sites now that no backend needs installing or
configuring.
- Simplify `brew doctor`'s `check_linux_sandbox` to report the
Landlock failure reason with the `$HOMEBREW_NO_SANDBOX_LINUX`
workaround.
- Since #23312, `brew services start` always regenerated the service
definition from the formula `service` block. Formulae that bundle
their own service file only declare `name` there, so the generated
plist had empty `ProgramArguments` and `launchctl bootstrap` failed
with `Input/output error`.
- Read the installed service file instead unless the `service` block
defines a command, the same gate `FormulaInstaller#install_service`
uses when writing generated service files into the keg.
- Fixes https://github.com/Homebrew/brew/issues/23408.
- The `base64` gem is no longer vendored so formulae and casks must
not use it; flag `require "base64"` and any `Base64` usage in
`Formula`/`Casks` files.
- Autocorrect `decode64`/`strict_decode64` to `String#unpack1` and
`encode64`/`strict_encode64` to `Array#pack`, matching the migration
in Homebrew/homebrew-core#296594.
- This reverts commit 10bdd6ba45.
- All formulae that needed `base64` now use native
`String#unpack1`/`Array#pack` after Homebrew/homebrew-core#296594
so the vendored gem is no longer needed.
- Bump `VENDOR_VERSION` as the vendored gem set changed again.
- `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 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).
- 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
- `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.
- 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.
The compatibility pass and zero-hook local migrations now have stable
cross-repository commits. Each can be reviewed as an independent pull
request.
- record dependency-ordered brew, core and cask commit hashes
- separate released compatibility aliases from canonical documentation
- retain merged tap zero-hook scans as the enforcement gate
- capture the final single-package DSL usage audit
The completed DSL work now has coordinated brew, core and cask stacks,
while
legacy-hook enforcement still depends on merged tap state.
- record every capability commit and its ordered tap consumers
- make the merged zero-hook scan a hard prerequisite for conflicts
- document the final usage audit and defer non-DSL hardening work
- require zero tap hooks before adding bridge conflicts
- record exact core and cask baselines and residual work
- reject named actions used by only one package
Formula and cask authors need one current reference while released
aliases remain an implementation-only compatibility bridge.
- document shared names, defaults, guards, tokens and command behaviour
- remove compatibility-only methods and values from the cookbooks
- explain ordered coexistence with legacy hooks during tap migration
Mechanical migrations should emit the canonical shared interface without
rejecting released compatibility names.
- emit canonical file, link, cache and service step names
- validate nested guards and regular-expression option nodes
- retain compatibility aliases during literal-block validation
- permit matching cask flight blocks during the migration bridge
Tap migrations cannot replace every cask flight block atomically.
Matching step and Ruby forms must coexist until the tap reaches zero
hooks.
- retain both artifacts instead of discarding either declaration
- execute declarative steps before the matching Ruby flight block
- use the shared command output default in every step phase
Formula and cask migrations need one predictable vocabulary while the
install-step surface already released from `main` remains compatible.
- use canonical file, link, cache, certificate and service names
- align explicit overwrite, removal, token and blank-value semantics
- preserve legacy move defaults and the former PATH lookup value
- retain shipped methods and values as commented compatibility aliases
- keep compatibility-only names out of public documentation
Five CPython and PyPy formulae share packaging bootstrap work that
depends on resources and implementation-specific install layouts.
- rebuild CPython site-packages, pip and wheel links from resources
- safely replace pre-existing Cellar site-packages directories
- preserve writable CPython venv activation templates
- preserve the Python 3.9 compatibility configuration
- stage PyPy resources and links for the required ABI