- `outdated_casks` treated a missing `version` as non-latest but then
reported that the latest version was already installed.
- Handle the missing version explicitly and reuse the narrowed value so
non-quiet output identifies the unavailable current platform.
- Strengthen the regression coverage for the warning path.
- Platform-specific casks can have no `version` when evaluated on an
unsupported operating system.
- Let upgrade detection skip `latest?` in that case so installation
reaches normal platform validation instead of raising `NoMethodError`.
- Cover a macOS-only cask evaluated on Linux.
Fixes#23498
- Keep `sha256` responsible only for selecting a checksum.
- Use OS and architecture `depends_on` declarations as the source of
truth for supported platforms.
- Reject missing platform checksums before any download and make
`--require-sha` reject both `nil` and `:no_check`.
Fixes#23494
Structured cask install steps run in a sandbox that denies network
access. This breaks casks whose upstream setup commands must retrieve
installation-time dependencies, including `gcloud-cli` virtualenv
creation.
- Add a serialised `network_access:` option to `run` steps.
- Omit the option from API data when it retains the default denial.
- Enable network access only when an explicit run requests it.
- Preserve network denial for existing steps and generated completions.
- Document the opt-in and cover both sandbox policies in tests.
This avoids disabling network restrictions for all cask operations while
giving affected casks an explicit, auditable escape hatch.
See #23495.
`brew upgrade` discovers outdated reverse dependants before it upgrades
the formulae selected for primary upgrade. This candidate list is also
needed for the confirmation prompt.
After the primary upgrades finish, Homebrew prepares installers for the
candidate dependants. An installer is filtered when installed runtime
dependencies already satisfy target bottle metadata. A candidate may
also already have been upgraded as a dependency of another primary
formula.
The execution heading and final summary used the earlier candidate list.
They therefore claimed a filtered dependant was upgraded although its
installed version remained unchanged. Candidates removed because
`FormulaInstaller.installed` contained them were also treated as
unsuccessful even when dependency installation had upgraded them.
- Build dependant installers before printing the execution-time list.
- Preserve installed candidates unless the primary list represents them.
- Merge those candidates with dependants upgraded by their installers.
- State the exact bottle-metadata condition when filtering a candidate.
- Cover filtered, dependency-installed and primary candidates in tests.
Fixes#23483
- reject mixing formula install hooks in every tap
- autocorrect legacy step methods and keywords
- retain stable serialised API values for compatibility
- document canonical Formula and Cask step authoring
- remove the resolved Cask sandbox deprecation reminder
- Discover CPython wheels already packaged in each keg.
- Expand PyPy bootstrap archives packaged in `libexec`.
- Resolve dependency paths without loading formula source.
- Validate helper executables before attempting to run them.
- Enforce source independence with RuboCop and API-loaded tests.
- Keep the internal JSON API unchanged and minimal.
- 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