-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Describe the bug
The Nix profile scripts unconditionally add directories to PATH
without checking if those directories are already there. The multi-user profile script has a guard variable to prevent it from running multiple times, but this guard variable is not exported and so does not affect subshell behavior. The single-user profile script doesn't even have a guard variable.
The effect of this is that running an interactive subshell (if multi-user, or a login subshell if single-user) will source the profile script and add the directories to PATH
again even though they're already there.
Steps To Reproduce
- Install Nix in an existing system
- Spawn a new shell. Verify that
$PATH
includes your nix profile bin directories. - Spawn a subshell (a login subshell if you have a single-user install)
- Check what
$PATH
contains
Expected behavior
$PATH
should contain your nix profile bin directories exactly once in both the parent shell and the subshell.
Additional context
I have not actually reproduced this problem locally, as I am not currently set up to test standalone Nix installs (the machines I have access to use either NixOS or nix-darwin and I'm not prepared to run a VM at the moment). But this can be verified by reading the profile scripts, and this issue was originally reported to me at lilyball/nix-env.fish#11 in my fish plugin that imports the bash profile.
One potential fix is to just export the __ETC_PROFILE_NIX_SOURCED
guard variable (and add this to the single-user profile too as that doesn't even have a guard variable right now), but I don't think that's a good fix as sudo
will preserve PATH
but not the other variables (and especially not the guard variable) and so running an interactive or login shell via sudo
would still add the duplicate PATH
entries.
Given that, I think the best approach is to just make sure the profile script is idempotent. I should be able to source it multiple times in a row (unsetting the guard variable each time) and end up with the same environment that I do if I only source it once. The guard variable still has a purpose with this approach, both as an optimization and to prevent re-sourcing the profile from blowing away any subsequent environment modifications (the installer sources the profile from potentially multiple files involved in shell initialization, and I might update the earliest such file to change the environment and so the subsequent re-sourcing of the profile shouldn't blow that away). Making the script idempotent simply requires making it check if the entries are in PATH
before setting them, as all other environment changes it does are already idempotent, though it's probably also worth putting a comment in the file noting that it should be idempotent as a caution for anyone modifying it in the future.