Homebrew / homebrew-bundle

📦 Bundler for non-Ruby dependencies from Homebrew, Homebrew Cask and the Mac App Store.
MIT License
5.36k stars 288 forks source link

Brewfile ordering is non-deterministic #1473

Closed injust closed 1 month ago

injust commented 1 month ago

I version control my Brewfile and the non-deterministic ordering makes the diffs a bit noisy. This seems to happen on and off with the python@3.1x formula.

I'd expect all the brew "..." entries in the Brewfile to be ordered alphabetically.

For example, some time ago, I upgraded from python@3.12 to python@3.13, ran brew bundle dump --force, and this was the Brewfile diff:

diff --git a/Brewfile b/Brewfile
index f8cad37..78ed7c8 100644
--- a/Brewfile
+++ b/Brewfile
@@ -3,7 +3,6 @@ tap "homebrew/bundle"
 tap "indirect/tap"
 tap "nrlquaker/createzap"
 tap "oven-sh/bun"
-brew "python@3.12"
 brew "asciinema"
 brew "bat"
 brew "croc"
@@ -47,6 +46,7 @@ brew "openssh"
 brew "p7zip"
 brew "pre-commit"
 brew "pv"
+brew "python@3.13"
 brew "ripgrep"
 brew "starship"
 brew "unar"

Later on, I installed the whatsapp cask, ran brew bundle dump --force again, and this was the diff:

diff --git a/Brewfile b/Brewfile
index 45bcca5..3a44757 100644
--- a/Brewfile
+++ b/Brewfile
@@ -5,6 +5,7 @@ tap "nrlquaker/createzap"
 tap "oven-sh/bun"
 brew "asciinema"
 brew "bat", args: ["HEAD"]
+brew "python@3.13"
 brew "croc"
 brew "ctags"
 brew "curl"
@@ -46,7 +47,6 @@ brew "openssh"
 brew "p7zip"
 brew "pre-commit"
 brew "pv"
-brew "python@3.13"
 brew "ripgrep"
 brew "starship"
 brew "unar"
@@ -80,6 +80,7 @@ cask "spotify"
 cask "tetrio"
 cask "transmission"
 cask "visual-studio-code"
+cask "whatsapp"
 cask "zed"
 mas "Command X", id: 6448461551
 vscode "bmalehorn.vscode-fish"
MikeMcQuaid commented 1 month ago

It's not non-deterministic. In this case it will be because croc or or similar gained a python@3.13 dependency which means it had to be installed before.

injust commented 1 month ago

Okay, that kind of makes sense, but I'm still confused. My Brewfile only lists the leaves and not their dependencies. So let's assume that python@3.13 is a dependency for an earlier formula -- why does that have to impact the order?

MikeMcQuaid commented 1 month ago

@injust because otherwise it will be installed when written and, if it fails, it will be confusing as to why it was installed in between bat and croc and not in between pv and ripgrep.

injust commented 1 month ago

Fair enough.

It's not non-deterministic. In this case it will be because croc or or similar gained a python@3.13 dependency which means it had to be installed before.

Is this direct dependencies only, or does it include transitive dependencies too?

If I try brew uninstall python@3.13, it errors and tells me that it's a dependency of glib (not in the Brewfile because it's a dependency) and yt-dlp. yt-dlp is down near the bottom of the list.

Also, croc doesn't have any non-build dependencies. So I still don't see why python@3.13 got moved to that line in the Brewfile specifically.

MikeMcQuaid commented 1 month ago

Is this direct dependencies only, or does it include transitive dependencies too?

It includes recursive dependencies.

So I still don't see why python@3.13 got moved to that line in the Brewfile specifically.

You can step through the code if you want to figure it out, it's all open source.

injust commented 1 month ago

In this case, python@3.13 shifted because of the dependency chain:

ffmpeg -> cairo -> glib -> python@3.13

glib recently updated its dependencies from python@3.12 to python@3.13, which moved python@3.13 "above" the (invisible) cairo entry in the Brewfile, hence its position between bat and croc.

because otherwise it will be installed when written and, if it fails, it will be confusing as to why it was installed in between bat and croc and not in between pv and ripgrep.

In this case, it feels like the toposort causes more confusion (but I haven't thought about cases where it would help).

MikeMcQuaid commented 1 month ago

In this case, it feels like the toposort causes more confusion (but I haven't thought about cases where it would help).

I have thought about cases where it would help which is why we have it. It causes less confusion (and I can state this as I've read every issue and comment ever made on this repository).