nix-community / nix-direnv

A fast, persistent use_nix/use_flake implementation for direnv [maintainer=@Mic92 / @bbenne10]
MIT License
1.78k stars 101 forks source link

Endless recursion when using with nushell hooks #464

Closed Granitosaurus closed 8 months ago

Granitosaurus commented 8 months ago

I'm reading the intro docs and it recommends using nushell hook like so:

    hooks: {
        env_change: { PWD: [
            { ||
                if (which direnv | is-empty) { return }
                direnv export json | from json | default {} | load-env
            }
        ]}
    }

However if default.nix contains a shellHook command to switch nix-shell to nushell from default bash like so:

with import <nixpkgs> {};
mkShell {
  shellHook = ''
    nu
  '';
}

endless recursion is caused. One way to get around this is to set an env variable in the nushell hook to stop the recursion like this:

{ ||
    if (which direnv | is-empty) {
        return
    } else {
        if ($env.DIRENVONCE? != "1") {
            $env.DIRENVONCE = "1"
            direnv export json | from json | default {} | load-env
        }
    }
}

but it seems like there should be some better way to do this?

Granitosaurus commented 8 months ago

Also I can't seem to find any docs on direnv export json as it doesn't even appear on --help and for some reason it hangs nushell for extended periods of time.

bbenne10 commented 8 months ago

I think you've mistaken the nix-direnv repository for the direnv repository, as we make no mention of nushell in our documentation at all. nix-direnv is an addon for direnv that caches the evaluation of environments by way of writing out the print-dev-env output and creating gcroots for inputs. direnv has a use nix and use flake builtin function that works well, but does neither of the above things and thus leaves the shell available for early garbage collection.

However, invoking a shell from within shellhook seems like an extraordinarily bad idea to me. Use -c/--command instead and this is a non-issue. The problem here is that we invoke commands that then invoke shellHook and expect it to be a hook that returns at some point. nix-direnv in particular is...finicky about shellHook. Because the shellHook runs in a bash subshell, we can't handle functions or aliases. Because we cache the evaluation of the bash subshell's environment, we explicitly need the subshell to exit to capture that environment. This means that:

As an aside: direnv export <shell> is a semi-internal helper that is largely used in initialization of shells. Check the output of direnv hook zsh. I suspect you're being pointed to use that because direnv lacks explicit nushell support, but I cannot say.

Granitosaurus commented 8 months ago

Thanks for the clarification @bbenne10 - this was very helpful! I was certain that -c and shellHook perform identical functionality but that turns out not to be the case which explains everything 🫣