ublue-os/bluefin

Homebrew's path setting conflict with host binaries

Closed this issue · 11 comments

Both my steam and discord flatpaks were erroring out. Discord wouldn't launch at all and steam was giving "invalid SSL certificate" error in the store page.

This led me to look at what I had installed: https://bbs.archlinux.org/viewtopic.php?id=252390

I found a copy of p11-kit in my homebrew installation. The problem is that this version of p11-kit takes precedence over the host, causing the error. Removing it fixed everything and all my flatpaks are working again.

Not sure how we should proceed but if this is the case there's probably a bunch of other stuff that could break if the homebrew version of a package takes precedence over the system one.

Perhaps we can start by putting homebrew's paths last so the host binaries always take precedence?

❯ printf '%s\n' "$PATH" | tr ':' '\n'
/home/linuxbrew/.linuxbrew/bin
/home/linuxbrew/.linuxbrew/sbin
/home/linuxbrew/.linuxbrew/opt/uutils-coreutils/libexec/uubin
/home/linuxbrew/.linuxbrew/bin
/home/linuxbrew/.linuxbrew/sbin
/home/linuxbrew/.linuxbrew/opt/uutils-coreutils/libexec/uubin
/home/linuxbrew/.linuxbrew/bin
/home/linuxbrew/.linuxbrew/sbin
/home/jorge/.local/bin
/home/jorge/bin
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin
/home/linuxbrew/.linuxbrew/opt/fzf/bin
/var/home/jorge/.local/share/JetBrains/Toolbox/scripts
/var/home/jorge/.local/share/JetBrains/Toolbox/scripts

Ok I followed this advice and replaced:

eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"

with

export PATH="${PATH}:/home/linuxbrew/.linuxbrew/bin"

This ends up with:

❯ printf '%s\n' "$PATH" | tr ':' '\n'
/home/jorge/.local/bin
/home/jorge/bin
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin
/home/linuxbrew/.linuxbrew/bin
/home/linuxbrew/.linuxbrew/opt/fzf/bin
/home/linuxbrew/.linuxbrew/bin

Which solves the problem, but then the issue gets flipped, if something installed via brew installs a dependency that also happens to be on the host the host's version will take precedence, which could be a problem.

Or maybe there's something we can pass along to flatpak itself to ignore the homebrew paths entirely?

I've run into similar issues. In my case, I was ok with brew paths before system, but not before $HOME/.local/bin etc.

I think this is generally the problem with "competing" package managers. Brew is pretty great, but it sometimes installs unexpected dependencies, which can lead to issues, masking host installed commands, etc.

Ultimately, I'm not sure if there is a generic solution, it seems somewhat "user choice". But if the worst offenses are solved by flatpak only using system path rather than user path, that's a start.

Or maybe there's something we can pass along to flatpak itself to ignore the homebrew paths entirely?

I haven't used flatpak much but it seems like flatpak override https://man7.org/linux/man-pages/man1/flatpak-override.1.html could help:

If the application ID APP is not specified then the overrides affect all applications, but the per-application overrides can override the global overrides.

There's a --env option for flatpak override - so need to pass the PATH to that

Tagging @ljrk0 as they have opinions on this.

Heyhey, thank you for tagging me!

Or maybe there's something we can pass along to flatpak itself to ignore the homebrew paths entirely?

I think this is very close to a real solution to the problem, except that it's flipped: Instead of adding the homebrew (or any other package manager's output) PATH to the environment and then stripping it from the Flatpak environment, one can just not add the PATH to the default/login environment (which is what Flatpak re-uses). Instead, the terminal emulator (or the chosen interactive shell) explicitly adds the utilities to the PATH.

Background

On login, the users login shell (chosen through chsh) executes and sets up the login environment. In case of bash, this means sourcing /etc/profile, ~/.bash_profile, ~/.bash_login and ~/.profile. All subsequent applications started/processes forked will inherit the environment set up through those means, including Flatpak'ed apps. We do not want to pollute this environment with tools installed through the home package manager!

When a terminal emulator starts it will also inherit this environment as it's just another application. In addition, it will start an interactive shell, either the same shell set-up as login shell, or a different one, changed in the profile settings of the terminal emulator. Subsequently, this shell will source all files relevant for interactive use, in case of bash ~/.bashrc.

Side note:
This explains why adding anything to ~/.profile will require a re-login for changes to apply, while adding the same to ~/.bashrc just requires a new shell instance to be spawned. However, changing either won't affect any applications not spawned from this shell as they are forked from the original, login shell instance, which hasn't had its environment changed.

Idea

Just as one can configure one owns terminal emulator to use a different shell for interactive use than the login shell, one could add PATHs to the environment as part of the terminal emulator profile settings. Environments changed this way would only affect shells started in this terminal emulator. One can extend this concept and even add multiple profiles with different shells and different PATHs for different utilities available. Basically this is simply an extension to the recommendation of changing your preferred shell through the terminal emulator rather than chsh (cf. https://tim.siosm.fr/blog/2023/12/22/dont-change-defaut-login-shell/ by @travier).

Variation #1

The whole concept is closely related to virtual environments, e.g., as seen in python-venv. These tools simply ship a different version Python and libraries in a location that is not "enabled" or "visible" by default, but sourcing my-venv/bin/activate will add them to PATH for the current session. Instead of managing this through the CLI, the original proposal manages them through the GUI.

A variation to this proposal would be to do this CLI only and have semantics similar to toolbox enter, e.g., homebrew enter. The only thing this command does is basically set up the PATH suchlike that the utilities from homebrew are the first in PATH.

Of course, the terminal emulator could be made aware of this and a hybrid option would offer the CLI tool for "unaware" terminal emulators/sessions (e.g., through SSH) but also allowing terminal emulators to launch "directly" into the "homebrew profile" or even different profiles for different purposes. Think homebrew enter dev, homebrew enter forensics, etc. which could receive different .desktop files for the terminal emulator to directly launch to. Functionality such as homebrew switch is also thinkable.

Variation #2

The simplest proposal with no need to develop new code is to simply rely on the shell's ability to distinguish b/w login and interactive shell. To recap:

  • A login bash sources /etc/.profile, ~/.bash_login, ~/.bash_profile and ~/.profile
  • An interactive bash sources ~/.bashrc

Further, it is common to "chainload" .bashrc by adding source ~/.bashrc in ~/.bash_profile, thus effectively making the set of environment variables added in the interactive shell mode a subset of the login shell ones. This is troublesome for us, since we want to add environment variables to the interactive mode only. However, in case of bash, we can simply check $i. @castrojo For your specific case, you could try using this in your ~/.bashrc (similar things exist for other shells too, I believe):

# Add homebrew tools to PATH in interactive mode only
[[ $- == *i* ]] && eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"

Further Thoughts

Of course homebrew may be used to install tools that also install .desktop files. These will be launched with the "default" environment inherited from the login shell and not from the "profile" they were installed from. One may argue (and I'd indeed do so) that such tools/applications are best installed via Flatpak or shouldn't install .desktop files as this is mixing concepts, but if this is a use-case worth solving, one would need to prepend the Exec= line in the .desktop file with something like homebrew run -p the-profile-name ...

Solved and shipped, thanks folks!!!!

I was curious about how this was fixed but none of the commits jumped out at me - I'm guessing this was fixed as part of the 2.0 release #947