upgrade: keep requested order in keg-only groups - #24092
Merged
MikeMcQuaid merged 1 commit intoSep 26, 2026
Merged
Conversation
Contributor
Author
|
small issue on non-macos builds/tests. I have a fix that I will push in a second after I thoroughly test it locally. |
zbeekman
force-pushed
the
upgrade-stable-keg-only-order
branch
from
September 25, 2026 18:20
f7fab17 to
1d981b5
Compare
Contributor
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The ordering fix is correct and covered by a focused regression test.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Preserves requested formula upgrade order while prioritising keg-only formulae.
Changes:
- Replaces unstable sorting with stable partitioning.
- Adds regression coverage for ordering.
| File | Description |
|---|---|
Library/Homebrew/upgrade.rb |
Stably groups keg-only formulae first. |
Library/Homebrew/test/upgrade_spec.rb |
Verifies relative ordering is retained. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Contributor
Author
|
The failing tests appear to be orthogonal to my changes:
|
zbeekman
force-pushed
the
upgrade-stable-keg-only-order
branch
from
September 25, 2026 19:25
1d981b5 to
20cd416
Compare
Contributor
Author
|
@MikeMcQuaid Should I take a stab at fixing the From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Izaak Beekman <zbeekman@gmail.com>
Date: Fri, 25 Sep 2026 16:00:00 -0400
Subject: [PATCH] tests: reset `Homebrew.auditing` between examples
- `brew audit` specs set `Homebrew.auditing` and never reset it, so
later `odeprecated` examples in the same process print nothing and
fail depending on the random seed.
---
Library/Homebrew/test/spec_helper.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb
index 100be27f84..6525a97b95 100644
--- a/Library/Homebrew/test/spec_helper.rb
+++ b/Library/Homebrew/test/spec_helper.rb
@@ -309,6 +309,7 @@ RSpec.configure do |config|
config.around do |example|
Homebrew.raise_deprecation_exceptions = true
+ Homebrew.auditing = false
Tap.installed.each(&:clear_cache)
Cacheable::Registry.clear_all_caches
--
2.51.0
|
- `Array#sort!` is not stable, so the order formulae were named in was scrambled within the keg-only and non-keg-only groups. - `partition` keeps keg-only formulae first and the requested order.
MikeMcQuaid
force-pushed
the
upgrade-stable-keg-only-order
branch
from
September 26, 2026 00:27
20cd416 to
7dcb151
Compare
MikeMcQuaid
approved these changes
Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

brew upgrade --formula a b c …doesn't keep the order the formulae were named in.upgrade.rb#L106-L116moves keg-only formulae first usingsort!with a comparator that returns0for formulae of the same kind. Ruby'ssort!isn't stable, so the requested order within each group gets scrambled. The dependency sort that follows (#L118-L124) keeps its input order, so the scramble reaches the install order.This PR uses
partition(&:keg_only?)instead. Keg-only formulae still go first, and the requested order within each group is kept. In my synthetic testing and benchmarking this was also ~20% faster thansort!although both took very little absolute time. I will try to runbrew benchmarkonce some long running LLVM and Flang builds finish. (The joys of still owning an Intel mac...)Why: on machines that build from source, users may want to order upgrades deliberately (e.g. quick builds before slow ones), and that order is currently lost.
Reproduce (prints
shfmt eza fzf jq ripgrep tree wget yq shellcheck bat):See also: #23551
brew benchmarkresults.brewcommands to reproduce the bug?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 disclosure: I used Claude Code (Claude Opus 5.5) to trace the ordering logic and to draft the reproduction, fix, test and this description. I reviewed all of it, confirmed the reproduction, and ran the new test (fails before the fix, passes after),
brew tests --only=cmd/upgradeandbrew lgtmlocally.New edits with some benchmarking numbers:
brew benchmarkHere are the results of
brew benchmarkalthough they don't seem to touch thebrew upgradecode we changed too much so their results are somewhat meaning less:brew benchmark sl figlet cowsay cmatrix fortune sipcalc(3 runs, Intel macOS):main(86650d0):This PR (20cd416):
Custom benchmarking
Micro-benchmark: keg-only ordering in
Upgrade.formula_installersSetup
brew ruby.upgrade.rb:106, benchmarked side by side in one script:sort!with the keg-only comparator block (currentmain)keg_only, not_keg_only = list.partition(&:keg_only?)thenlist.replace(keg_only + not_keg_only)list.replace(list.partition(&:keg_only?).flatten(1))keg_only?, about 20% keg-only (srand(42)).Formulaobjects for every name in my Cellar: 492 loaded (7 from untrusted taps skipped), 33 keg-only.Benchmark.bmbm(rehearsal pass, then measured pass). Each repetition reorders a freshlist.dup; the copy costs the same for all three.partitionversions keep the original order within each group;sort!does not.Results (time per call, measured pass)
sort!The PR's version is as fast as or faster than
sort!at every size, and about twice as fast on realFormulaobjects, where eachkeg_only?call costs more andpartitionmakes n calls wheresort!makes up to four per comparison over O(n log n) comparisons.brew benchmark(install and fetch workloads) doesn't run this code: its install runs setHOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK, soformula_installersreturns early atupgrade.rb:104. Its results formainand this PR are within run-to-run noise.