direnv / direnv

unclutter your .profile
http://direnv.net
MIT License
12.6k stars 641 forks source link

direnv load on new shell #614

Open thernstig opened 4 years ago

thernstig commented 4 years ago

Is your feature request related to a problem? Please describe. If I go to a directory for the first time, direnv properly loads my .envrc content. But if I open a new shell directly with e.g. fish, while inside the directory I just went to, it does not load my .envrc content again. The problem with this is that if I have PATH prepends done in .config/fish/config.fish for non-login shells, they they take precedence over my project specific PATH_add in my .envrc.

One option one might think is to change my config.fish to only add to PATH in login shells. This is not a great solution, as e.g. as an example VS Code's integrated terminal when launched is not a login shell, meaning my shell specific PATH changes are never run (which is needed).

Describe the solution you'd like Wouldn't it be a good default for direnv to load the env if launching a new shell from current shell? It should not use direnv reload as that requires me to "press Enter to get a new prompt" in all other shells I have open to reload the environments.

Describe alternatives you've considered I could add a direnv reload to the bottom of my .config/fish/config.fish, but it has the effect I need to "press enter" to get a new prompt in all other open shells. That also has the unfortunate side-effect to output direnv: error .envrc not found when opening a shell where an .envrc does not exist.

pjeby commented 4 years ago

If you want to reload only within your current shell, you can unset DIRENV_WATCHES and/or DIRENV_DIR. This will make direnv assume it needs to reload (because the file time is different and/or you're in a new directory), and you can definitely do the unsetting in your fish config.

A note of caution, though: this will undo any PATH changes made by that config, or by fish itself.

That is, when direnv reloads due to the missing vars, it will restore the value of .envrc-changed variables that they had before any manipulation the new shell did, then re-apply the .envrc changes. If you don't want this to happen, and instead want to apply the .envrc changes to the current environment without first reverting those variables to the previous shell's pre-.envrc values , then you'll also need to unset DIRENV_DIFF.

Finally, none of this is officially endorsed or documented AFAIK, I just know these things because I've been knee-deep in the direnv source code the past couple days. ;-) They are internal implementation details that might change in a future release, especially if there ever ends up being an official way to do this instead, like eval "$(direnv reload bash)" or direnv reload fish | source.

pjeby commented 4 years ago

Also, I just remembered: recent versions (i.e. certainly 2.21.3, possibly earlier) will reload on running direnv allow. But it would have the same problems with reverting variables.

Probably what you really want is something like:

eval (pushd /; direnv export fish; popd;)

At the top of your fish config. Assuming you don't have an .envrc in /, this will let you unload the current envrc before you do any PATH manipulation. That way, you'll be munging a clean environment, and then when you get to the direnv hook, it'll go, "oh, you just changed here from /, so let's load this new config."

One important bit, though: direnv export is considered private (internal to the hook), so this is definitely still in undocumented-and-might-break-in-future territory. Probably there should be an official way to do this, though.

thernstig commented 4 years ago

@pjeby I much appreciate the workarounds. I wish the most this was fixed directly in direnv though. This is a big problem e.g. if using something common like VS Code shell, where you can spawn new terminals. When a new fish shell opens in a new terminal, it will load the config.fish but not reload direnv, meaning project-specific PATH additions are not in effect anymore. So I find it a real, lacking feature of direnv to handle this scenario.

thernstig commented 4 years ago

I just found this https://github.com/direnv/direnv/issues/583 and do believe this is a duplicate. Will keep this one open for a while more before closing just in case.

edit: Actually, this might be different, but I would have to check once https://github.com/direnv/direnv/issues/583 has been implemented/fixed to see how it affects my scenario here.

wderezin commented 3 years ago

I implemented issue #583 in PR #732 and it does not solve this issue.

However, VS Code now works when spawning a new terminal.

And to implement the alternative you can add the following to your .config.fish

set -q DIRENV_DIR; and direnv reload
thernstig commented 3 years ago

I want to be clear I can do workarounds, but my request is a feature request to not have to do workarounds. I think the request is sensible for everyone.

wderezin commented 3 years ago

I don't see a way for direnv to universally detect there is a new shell. A new shell inherits all the global environment variables which direnv uses to trigger load/reload.

However, if you change the requirement to 'direnv reload on hook' this could work through setting an environment variable such as DIRENV_RELOAD_ON_HOOK that each shell's hook could implement.

Fish for example could add the following to the direnv hook fish output:

  set -q DIRENV_DIR
  and test  "$DIRENV_RELOAD_ON_HOOK" = "1"
  and command direnv export fish | source
zimbatm commented 3 years ago

The only "solution" I have is to make sure the environment is unloaded before spawning the sub-shell. We have the same issue with Tmux (see wiki). Basically, alias fish='direnv exec / fish'.

Even better would be for fish to not change environment variables on load.

thernstig commented 3 years ago

Even better would be for fish to not change environment variables on load.

This is not possible. Fish loads .config/fish/config.fish (equal to .bashrc) which loads on shell startup and many users set en vars there. One could argue users should only set it when it is a login shell though.

zimbatm commented 3 years ago

Agreed. It would take a big campaign to sensibilize users to only set env vars on login shells. Most users probably don't even know the difference between those two.