Skip to content

formula: deuniversalize the keg being installed - #24327

Merged
MikeMcQuaid merged 2 commits into
Homebrew:mainfrom
zbeekman:fix-deuniversalize-upgrade
Oct 23, 2026
Merged

MikeMcQuaid merged 2 commits into
Homebrew:mainfrom
zbeekman:fix-deuniversalize-upgrade

Conversation

@zbeekman

@zbeekman zbeekman commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

AI use: Claude Opus 5.5 (high effort), in Claude Code. It traced the
balena-cli upgrade failure to any_installed_keg, then drafted the
fix, the spec, and this description. I reviewed the diff and the
formula.rb code paths it relies on (prefix, any_installed_prefix,
Keg#mach_o_files).

Verification (macOS 26, Intel):

  • New spec fails without the fix (RuntimeError: No universal binaries found to deuniversalize) and passes with it.
  • brew lgtm passes (typecheck, style, and brew tests --changed).
  • brew tests --only=formula,os/mac/formula,formula_installer,keg: pass.

deuniversalize_machos with no arguments looks for universal binaries in
any_installed_keg. When the formula is already installed, that returns
the opt-linked keg. During a source upgrade, the opt-linked keg is the
previous version, whose binaries were already thinned when it was
installed. So the call finds nothing and raises, and the build fails at
its last step:

Error: balena-cli: An exception occurred within a child process:
  RuntimeError: No universal binaries found to deuniversalize

Even if it had not raised, the new keg would have been left universal.

This affects any formula that calls deuniversalize_machos with no
arguments, on any macOS architecture, whenever it is upgraded from
source. Bottling CI installs into a clean prefix, so it never sees an
old keg and never hits this.

The fix searches prefix instead. Inside install, prefix is the
versioned keg being built. Outside install, it still resolves to
opt_prefix when the formula is opt-linked, as before.

Reproduce

With an older balena-cli installed (any formula that calls
deuniversalize_machos with no arguments behaves the same):

brew upgrade --build-from-source balena-cli

brew reinstall --build-from-source of the installed version does not
reproduce it (checked with balena-cli 25.2.7 on unpatched brew):
reinstall renames the old keg to <version>.reinstall, and the opt link
then points at the new keg being built at the same path.

Tests

Added a macOS spec. An opt-linked 1.0 keg holds a thin dylib and the 2.0
keg holds a universal one. The spec checks that deuniversalize_machos
thins the 2.0 dylib. It fails with the old code, which raises the error
above.

- With no targets, `deuniversalize_machos` searched `any_installed_keg`,
  which returns the opt-linked keg when one exists.
- During a source upgrade that is the previous version's keg, already
  thinned, so the call raised "No universal binaries found to
  deuniversalize" (e.g. `balena-cli` 25.2.6 -> 25.2.7). Had it not
  raised, the new keg would have stayed universal.
- Bottling CI installs into a clean prefix, so it never hits this.
- Search `prefix` instead: inside `install` it is the versioned keg
  being built.
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0326b542-d9f5-4652-ae04-7833c927b461
📥 Commits

Reviewing files that changed from the base of the PR and between 4113f56 and 4162373.

📒 Files selected for processing: 2
  • Library/Homebrew/formula.rb
  • Library/Homebrew/test/os/mac/formula_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Fixed handling of universal macOS binaries during installation when an older version is already installed. The binaries are now selected from the formula’s current installation when available, rather than being affected by files from an existing installation. If the formula’s current installation is absent, the existing fallback behaviour is retained. This ensures binaries are thinned for the intended installation in both cases.

When no targets are supplied, Formula#deuniversalize_machos searches the current formula prefix if it exists. If it does not, the method falls back to any_installed_keg. Tests cover both selection paths.

Changes: Universal Mach-O selection

Layer / File(s) Summary
Select binaries from the current prefix
Library/Homebrew/formula.rb, Library/Homebrew/test/os/mac/formula_spec.rb
Automatic target discovery uses Keg.new(prefix) when the prefix exists, and falls back to any_installed_keg when it does not. Tests check that the selected dylib is thinned to x86_64 in both cases.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 41623

The reported source-upgrade failure is addressed, with no remaining identified merge-blocking risk.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly identifies the main change: deuniversalize_machos now targets the keg being installed.
Description check Passed The description explains the bug, the cause, the fix, reproduction steps, tests, and verification results. It is directly related to the changeset.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0ec05cce-05ad-411a-b6b0-dde7b91b9934
📥 Commits

Reviewing files that changed from the base of the PR and between 9811f64 and 4113f56.

📒 Files selected for processing: 2
  • Library/Homebrew/formula.rb
  • Library/Homebrew/test/os/mac/formula_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread Library/Homebrew/formula.rb Resolved

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Coderabbit comment matches my concern.

@zbeekman

Copy link
Copy Markdown
Contributor Author

Thanks! Coderabbit comment matches my concern.

@MikeMcQuaid I can implement the code rabbit suggested fix, but it seems like it's basically unreachable code to me. I'm happy to do whatever you prefer.

      # Prefer `prefix` to `any_installed_keg`: during an upgrade the latter
      # is the old opt-linked keg rather than the keg being installed.
      keg = prefix.directory? ? Keg.new(prefix) : any_installed_keg
      targets = keg&.mach_o_files&.select do |file|

- Outside `install`, `post_install` and `test`, `prefix` may not exist
  when only an older version is installed.
- Search `any_installed_keg` in that case, as before, instead of
  raising from `Keg.new`.

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@MikeMcQuaid
MikeMcQuaid enabled auto-merge October 22, 2026 17:14
@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Oct 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Oct 22, 2026
@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Oct 23, 2026
Merged via the queue into Homebrew:main with commit 206de9f Oct 23, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

⌂ 换网址