Closed P1n3appl3 closed 1 year ago
I removed nix-your-shell and checked with the vanilla nix-shell and it doesn't have this issue, though I'm not sure how it avoids sourcing
nix-daemon.sh
.
I think the answer is that it doesn't π
β― tr : "\n" <<<$PATH
/Users/me/.nix-profile/bin
/nix/var/nix/profiles/default/bin
/usr/local/bin
/System/Cryptexes/App/usr/bin
<snipped>
β― \nix-shell -p hello --command 'tr : "\n" <<<$PATH'
/nix/store/6886788jq61kq2blrhs70w4y63pq35ys-bash-interactive-5.2-p15/bin
/nix/store/vlh4h7796gzks5kwlcvmcgr2ldp1xz7q-clang-wrapper-11.1.0/bin
<snipped: more stdenv paths>
/nix/store/44x762l9fgrbb5j8cy1wp6azc1ana7i3-hello-2.12.1/bin
/nix/store/7lqsqgisdrkq9ymfigjf9m0jnxwj1m9w-coreutils-9.1/bin
<snipped: even more stdenv paths>
/Users/me/.nix-profile/bin # dup!
/nix/var/nix/profiles/default/bin # dup!
/Users/me/.nix-profile/bin
/nix/var/nix/profiles/default/bin
/usr/local/bin
/System/Cryptexes/App/usr/bin
<snipped>
Because nix-shell
/nix develop
prepend to $PATH and run their setup within the shell they spawn it's not an issue; the env setup script gets the last say.
For any-nix-shell
/nix-your-shell
β which use --command
to spawn the desired shell after the env setup script has run (1, 2) β this is observable.
Beyond $PATH
ordering discrepancies/non-exported vars not persisting, the --command
approach also means that non-exportable bash things (aliases, arrays, functions usually, etc.) also don't persist. For nix shell
type use cases (i.e. "I just want to put things on my $PATH") and probably most nix-shell
/nix develop
use cases (i.e. wanting exportable env vars like XDG_DATA_DIRS
, PYTHONPATH
, etc. to be modified) this is totally fine. But users with slightly more exotic use cases like using nix-shell
to build derivations or using mkShell
's shellHook
to provide bash functions, aliases, etc in devShell
s will notice this discrepancy.
If you'll excuse the tangent, I want to call out a couple of alternatives to --command
that address these discrepancies; this isn't exactly what @P1n3appl3's issue is about but I think it's still related:
nix print-dev-env
directlyMy understanding is that nix print-dev-env
dumps the setup script that nix develop
/nix-shell
/other facsimiles use.
Mapping nix develop
/nix-shell
commands to nix print-dev-env
(nix-your-shell
already does the work of parsing args in a rigorous way π) and then sourcing the script emitted in the shell that's spawned (i.e. --rcfile
for bash) would address all of the discrepancies above; afaik for bash this should be entirely equivalent to running nix develop
directly.
The big downside to this approach is that nix print-dev-env
itself is somewhat tailored to bash and the stdenv
machinery (which will very likely land in the script print-dev-env
emits) is very bash specific.
We definitely have the ability to post-process the env script we get out of print-dev-env
but there's a lot of surface area to try to make zsh/fish/etc compatible (arbitrary shellHook
s in projects also probably tend to be bash specific) and the failure modes generally aren't graceful.
Note FWIW,
zsh -c "source <(nix print-dev-env nixpkgs#hello)"
does work for me with one modification (LINENO
cannot be reassigned -- this is probably the kind of tweak we could get upstreamed) but this probably isn't indicative; I'm not familiar withfish
but I'd expect that it would require many more tweaks.Also, the value here over just having the exportable env vars is dubious; the
stdenv
functions and machinery are present but actually trying to use any of it breaks immediately.
nix print-dev-env
selectively, via bash
This is more or less the nix-direnv
approach; i.e.:
nix print-dev-env
direnv show_dump "$(direnv dump)"
direnv exec <dir> bash
actually works like nix-your-shell
/any-nix-shell
do; morally equivalent to using --command
Like the --command
approach this will not preserve aliases/arrays/functions/etc from the "actual" underlying bash shell.
The upside here is that because we have explicit control over the exportable env vars we're adding to the final bash/zsh/fish/etc shell we can choose to, for example, modify env vars after regular shell init has happened. This would fix issues like the OPs as well as this general class of issue (i.e. I source ~/.cargo/env
in my .bashrc
but don't want that to shadow nix provided binaries in a nix-shell
).
nix-your-shell
could go down this route by leveraging direnv
(there'd still be value to using nix-your-shell
; doing echo "use nix -p python3" > .envrc && direnv allow
instead of nix-shell -p python3
isn't exactly great UX).
Alternatively, there's also some prior art here re: using a bash subprocess to extract env vars without using all of the direnv machinery.
Ultimately, what option makes the most sense depends on the goals of the project.
Given the typical use cases for nix develop
and friends I think it probably doesn't make sense to pursue the first option (though it might be nice to provide some escape hatch or to have nix-your-shell
behave differently for bash so nix-shell
can still be used to debug drvs? not sure).
I think the second option aligns pretty well with the common use case β but I think it's totally reasonable to decide that the added complexity isn't worth it and that ad-hoc fixes for the common pitfalls (i.e. adding export __ETC_PROFILE_NIX_SOURCED
to env.fish
and env.sh
) are sufficient to provide a good user experience.
Also, I want to echo @P1n3appl3: thanks for making nix-your-shell
! I really appreciate the clear docs and source code.
Hopefully this comment wasn't too much of a diversion and is of some value!
Hey, sorry for the delay on this and thanks for the detailed reporting here! I had uh, forgotten to set this repository to watched so I wasn't getting notifications. Let me read the rest of this thread...
This issue seems pretty hairy! I've actually had a workaround myself β some code in my config.fish
conditionally sources stuff depending on if I'm in a nix-shell
or not. (I carried this over from any-nix-shell
.)
The big downside to this approach is that
nix print-dev-env
itself is somewhat tailored to bash
Yeah, agreed. I do not really want to figure out how to adapt that stuff to fish
and other shells. (I think it would also make adding new shells much more difficult.)
The second approach, sourcing nix print-dev-env
in a bash shell and transferring those environment variables over to the new shell, seems a little better.
Like the
--command
approach this will not preserve aliases/arrays/functions/etc from the "actual" underlying bash shell.
I think this is more or less fine. It would be really nice to preserve aliases/functions/etc. but probably more trouble than it's worth. When I need those I usually just run command nix-shell ...
and deal with bash for the duration.
Both of these options require a fair amount of work, though. Exporting __ETC_PROFILE_NIX_SOURCED
would be easy, so maybe I'll do that just to get something out the door? I'm not sure if there would be any unintended consequences to that.
Should I close this now that we have $__ETC_PROFILE_NIX_SOURCED
exported, or wait until we get a more thorough solution implemented?
For transparency, I have no scheduled plans to implement a more thorough solution, and I'm a little hesitant to increase the complexity of this project so substantially, but I don't want to write the possibility off entirely β it might be a really good+useful idea!
The workaround is good enough for me, falling back to command nix-shell
when i need the functions is fine for my use case.
tl;dr: maybe nix-your-shell should export the
__ETC_PROFILE_NIX_SOURCED
variable?I recently noticed that if you've got something installed in your profile, it will take precedence over packages added to a nix-shell when using nix-your-shell. For example I've got python 3.10 in my nix profile:
But when I enter a nix-shell with python 3.9, the 3.10 one is still earlier in my path
This happens because
~/.nix-profile/bin
and/nix/var/nix/profiles/default/bin
get added to$PATH
in/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
(which is sourced in/etc/zsh/zshenv
in my nix installation) . Looking in there it checks a var (__ETC_PROFILE_NIX_SOURCED
) before executing, so it should only be run once per shell. But since they just assign the variable instead of exporting it, starting a subshell ends up re-sourcing that script. Manually exporting that var is enough to prevent it from running the script and results in the expected behavior:I've exported the var in my
.zshrc
for now, but it seems like the sort of thing that nix-your-shell should do for you. I removed nix-your-shell and checked with the vanilla nix-shell and it doesn't have this issue, though I'm not sure how it avoids sourcingnix-daemon.sh
.Also thanks for writing this! I'm new to nix and not being able to use my zsh configuration was making trying it out way more annoying.