Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to disable direnv automatic loading of environments in the current shell? #550

Open
jraygauthier opened this issue Jan 16, 2020 · 18 comments · May be fixed by #1048
Open

How to disable direnv automatic loading of environments in the current shell? #550

jraygauthier opened this issue Jan 16, 2020 · 18 comments · May be fixed by #1048
Labels

Comments

@jraygauthier
Copy link

I cannot find any environment variable for doing this. I know that I can specifically deny a specific env from loading but that implies mutations of the configuration which I do not want to change my direnv configuration.

I would have expected something like:

export DIRENV_DISABLE=1
cd /my/project/whose/env/is/allowed
# .. Nothing.

I tried something like this in my ~/.bashrc:

if command -v direnv >/dev/null 2>&1 \
      && ! [ "1" = "${DIRENV_DISABLE:-null}" ]; then
    eval "$(direnv hook bash)"
fi

however, I cannot disable it temporarily as follow:

export DIRENV_DISABLE=1
cd /my/project/whose/env/is/allowed
# .. Nothing.
unset DIRENV_DISABLE
cd /my/project/whose/env/is/allowed
# Loading env

I feel that there should be 2 possibilities: DIRENV_DISABLE to disable to whole hook (no printing that the env is denied and that one can allow it) and another var such as DIRENV_DENY_ALL which would have the same effect as if all env were denied.

@jraygauthier
Copy link
Author

jraygauthier commented Jan 16, 2020

A DIRENV_DENY="/path/one:/path/two" would be helpful too in some use cases.

In a project, I currently offer a way to enter a combined nix environment so that the user can load his ide (vscode) from this environment and work on multiple projects (this environment can be optionally loaded via direnv). However, when a terminal is opened in some of these folders, direnv breaks the combined environment / needlessly takes time to load an environment that is already available through the combined nix env.

In this particular case, I would like to be able to deny / prevent direnv from automatically loading the environment but only for the projects that are part of the combined environment. That is so that the user can still get the advantages of direnv for any other projects.

@pjeby
Copy link
Contributor

pjeby commented May 6, 2020

Note that you can implement these things yourself in your .direnvrc or a ~/.config/direnv/lib/00-disable.sh , e.g. using:

${DIRENV_DISABLE:+exit}

This will basically treat any current .envrc as a no-op whenever DIRENV_DISABLE has a non-empty value. (Expanding this implementation to disable only specific directories is left as an exercise for the reader. 😉)

(Note: if you want to re-enable .envrc loading for the current directory, you may have to explicitly direnv allow or unset DIRENV_DIR as well as emptying or unsetting DIRENV_DISABLE, as direnv will otherwise consider the disabled .envrc to have already been loaded.)

@jraygauthier
Copy link
Author

@pjeby : Thanks for this trick, it is indeed working fine, and a nice think to know.

However, I feel like this should be shipped with direnv by default. This works fine for me, however, as this is some custom local direnv lib, I cannot assume it will be the case for my teammates, contributors so I cannot rely on this, say for example if I want to implement for example a nix shell hook that will prevent direnv from loading when I explicitly entered the nix env from via nix instead of direnv. It will only work for me but not my teammate, unless I can force them to install my lib.

@pjeby
Copy link
Contributor

pjeby commented Jun 13, 2020

I'm confused. If you want to disable direnv in a shell whose environment you control, why not just redefine or unset the hook function for that shell? As long as you do so before the first prompt is reached in that shell, that would disable it. In the case of e.g. bash, setting BASH_ENV to point to a file you control would suffice to do this, I would think.

@jraygauthier
Copy link
Author

Sorry for the confusion.

Just coming back to the following previous example: #550 (comment).

In this previous example, I only want to disable a select set of direnv directory whose environments are already merged in the top level combined environment. The lib above trick would effectively allow me to do this were it possible to make it available to my coworkers (for example through an env specifiying common libs).

Outside of this example, I certainly do not want to completely prevent my teammate's bashrc from being executed as this encode some of his personal preferences.

@pjeby
Copy link
Contributor

pjeby commented Jun 13, 2020

Then can't you write the .envrc files to not do expensive things if they're "already merged"? This discussion is still way too abstract for me, as it's unclear who owns what files.

(Also, I didn't say not to run .bashrc; I was just noting that as long as you can run code after .bashrc but before a prompt, you can disable direnv by removing it from PROMPT_COMMAND. Or you can alias direnv=my-direnv-function and define a wrapper function to implement DIRENV_DISABLE or DIRENV_DENY)

(Also, to be clear, I am not arguing against having the ability to disable direnv; just trying to suggest possible workarounds in the meantime.)

@jraygauthier
Copy link
Author

@pjeby: I'm in the context of the use nix directive. Using nix, it is possible to compose multiple environments for some lower level dependencies of a project so that I can get the overall set of dependencies which allows devs to get for example their IDE launched in an environment allow them to work on any of these dependencies + the main project.

Now assuming the following simplified tree where . correspond to the root of mytoplevelproject whose .envrc has content use nix (thus entering the ./shell.nix nix environement) and the nix environment subsume both subdep1/shell.nix and subdep2/shell.nix environements:

.
├── shell.nix
├── .envrc
├── subdep1
│     ├──shell.nix
│     └── .envrc
└── subdep2
        ├──shell.nix
        └── .envrc

Dev is doing:

$ cd mytoplevelproject
# direnv loading ./.envrc
$ launch-my-ide

Now, when inside the ide (which does have a shell), or when directly in the shell,
as I known that .envrc subsume both ./subdep1/.envrc and ./subdep2/.envrc, I do want a way to explicitly say that when already in the .envrc env, direnv won't trig when user changedir to one of the subdep:

$ cd ./subdep1
# Dir env does not load `.envrc, and ideally does not report an error when .envrc is now allowed.
$ cd -
$ cd ./subdep2
# Dir env does not load `.envrc`, and ideally does not report an error when .envrc is now allowed.

Indeed those are really nice workaround going well beyond my personal shell internals experience 😉. Will keep those in my toolbox and take some time to decant this new knowledge to see if I can effectively apply it as a workaround.

@todd-a-jacobs
Copy link

A more general use case for this is when you have things that you want to run in multiple directories, but temporarily don't want the current environment overridden by .envrc when using cd. While it's certainly possible to write a shell script that doesn't hook direnv, or use an alternate shell rc file, this can be burdensome if you just want to do something like:

export DIRENV_DISABLE=1
for dir in some_glob*; do
    cd "$dir"
    :
done
unset DIRENV_DISABLE

to execute some set of commands without triggering direnv for the lifetime of the loop. I'm actually trying to do something along those lines myself, and ran across this feature request. I would really love to have a way to temporarily unload or disable direnv from the shell, without having to load a completely different shell configuration to do so.

@nikhilkalige
Copy link

Is there is specific version I need to use for this. I am on

$ direnv --version
2.28.0

I exported the variable, but direnv still seems to be doing the load and unload part.

$ echo $DIRENV_DISABLE 
1

$ cd /tmp/
direnv: unloading

$ cd -
direnv: loading .envrc
direnv: export +AR

@todd-a-jacobs
Copy link

todd-a-jacobs commented Oct 1, 2021

Is there is specific version I need to use for this. I am on

@nikhilkalige This isn't actually a current feature. My example was essentially pseudo-code based on other comments earlier in the thread. As of today, there is no obvious facility for temporarily (or permanently) unhooking direnv execution other than starting a new shell without direnv loaded, or deleting all its related functions from your current shell.

One comment provided a potential Bash-centric solution for ignoring .envrc files when triggered, but I personally couldn't get it to work in either Bash or Fish (the latter being my preferred shell). Your mileage may vary.

@zimbatm
Copy link
Member

zimbatm commented Oct 3, 2021

What I usually do if I want to disable direnv in a single bash shell (for testing): cd / && unset PROMPT_COMMAND && eval "$(direnv export bash)" && cd -. Essentially removing the hook for the shell.

Look at direnv hook <shell>, each shell has a different hook that executes before the prompt.

@todd-a-jacobs
Copy link

What I usually do if I want to disable direnv in a single bash shell (for testing): cd / && unset PROMPT_COMMAND && eval "$(direnv export bash)" && cd -. Essentially removing the hook for the shell.

Look at direnv hook <shell>, each shell has a different hook that executes before the prompt.

I haven't tried this yet, but will test it. If it can be Fish-ified (considering global, universal, et al. variables), then I think this is a reasonable thing to do. Thanks for the suggestion; I'll let you know if it works for me.

@yermulnik
Copy link

yermulnik commented Oct 3, 2021

Basically direnv is just yet another app which leverages PROMPT_COMMAND.
The other popular use case for it is e.g. Git Prompt.
So unsetting PROMPT_COMMAND would mean you break everything which uses it to bring benefits to your terminal.
Another very personal example could be my own PROMPT_COMMAND:

> echo $PROMPT_COMMAND | wc -c
4913

It's only almost 5K chars, which means I would be very upset loosing all functionality I use by its mean.
So what I would suggest is based on what @zimbatm helped with some time ago for an unrelated use case:
replace eval "$(direnv hook bash)" with:

_direnv_hook() {
  local previous_exit_status=$?;
  eval "$(direnv export bash)";
  return $previous_exit_status;
};

(most probably local ... and return ... can be dropped in your use case)
and add/remove to/from PROMPT_COMMAND if you want to enable/disable it. Something like this to disable (not tested though; bash):

if [[ "${PROMPT_COMMAND:-}" =~ _direnv_hook ]]; then
        export PROMPT_COMMAND=${PROMPT_COMMAND//_direnv_hook/}"
fi

Hope this helps.

@zimbatm
Copy link
Member

zimbatm commented Oct 4, 2021

@todd-a-jacobs For fish, I think it's:

functions --erase __direnv_export_eval && functions --erase __direnv_export_eval_2

Both for the bash and fish solution, the commands are entered in the current shell. To restore direnv, exit and re-create a new shell. This is a solution for temporary testing, not a generic solution.

@todd-a-jacobs
Copy link

todd-a-jacobs commented Oct 4, 2021

functions --erase __direnv_export_eval && functions --erase __direnv_export_eval_2

@zimbatm Does this actually restore the environment first, or does it simply (as seems to me) erase the functions from the environment? It's serviceable either way, but if the latter then one has to be careful to restore the desired environment before disabling the functions, or you may still end up with a polluted environment.

Restoring direnv should be as simple as calling direnv hook fish | source in the current shell again, but I still think it would be better if direnv had a simple "restore real environment, and treat .envrc files as a no-op" option or environment variable. I'm not enough of a Go person to offer a possible implementation, but I think that's the behavior at least some of us are looking for here.

@zimbatm
Copy link
Member

zimbatm commented Oct 5, 2021

Time is limited for everybody, unfortunately. If somebody wants to tackle this problem in a PR, I'm happy to review it. Maybe start with a prototype so we can agree on the design before making the whole thing.

@ezmiller
Copy link

ezmiller commented Jul 6, 2022

+1

@reegnz
Copy link
Contributor

reegnz commented Jan 11, 2023

I have run into this problem as well. I've solved this with the following little shell function for zsh, if anyone might find it useful:

source $(direnv hook zsh)
direnv-toggle() {
  if [ -z "${DIRENV_DISABLE:-}" ]; then
    unset -f _direnv_hook
    export DIRENV_DISABLE=1
  else
    unset DIRENV_DISABLE
    source $(direnv_hook_zsh)
  fi
}

Would be great if direnv would respect the DIRENV_DISABLE environment variable out of the box though.
I'll try opening a PR for it, should only include some shell wrangling of the _direnv_hook function.

I mean, I DO want the polluted environment (eg. set the environment with eval $(direnv export bash)) but then I want the environment to be kept even if direnv is now disabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

8 participants