cached-nix-shell
cached-nix-shell copied to clipboard
shellHook behavior
Given the following cached-shell.nix
:
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
shellHook = ''
echo "Initialize something here (maybe a Postgres Database, for example)"
export SOMEVAR="$OTHERVAR"
'';
}
This is how nix-shell
behaves:
$ OTHERVAR=201 nix-shell cached-shell.nix
Initialize something here (maybe a Postgres Database, for example)
$ echo $SOMEVAR
201
And this is cached-nix-shell
(the echo
command outputs nothing both with a cached shell and when caching it the first time):
$ OTHERVAR=201 cached-nix-shell cached-shell.nix
$ echo $SOMEVAR
So I think there's a difference in behavior. Would it be correct to say that the correct behavior for cached-nix-shell
should be:
- Cache a derivation which is the shell derivation without its
shellHook
attribute. However, take care to cache dependencies that might be brought in by${some-nix-var}
inshellHook
's body. - Run
shellHook
everytimecached-nix-shell
runs.
Thanks in advance!
Yep, there are some differences in behavior caused by shell hooks.
However, the 100% correct behavior would be more complicated than the way that you described, because:
-
shellHook
is not the only hook that gets executed when you runnix-shell
. Another hook issetup-hook
. In the following example,$PYTHONPATH
variable is set by the setup-hook ofpython3
.$ cat ./shell-with-python3.nix { pkgs ? import <nixpkgs> { } }: pkgs.mkShell { buildInputs = [ pkgs.python3 ]; } $ nix-shell ./shell-with-python3.nix --run 'echo $PYTHONPATH' /nix/store/fjgnz0xfl04hsblsi4ym5y5akfh6mlmy-python3-3.8.5/lib/python3.8/site-packages:/nix/store/fjgnz0xfl04hsblsi4ym5y5akfh6mlmy-python3-3.8.5/lib/python3.8/site-packages $ PYTHONPATH=/somedir nix-shell ./shell-with-python3.nix --run 'echo $PYTHONPATH' /somedir:/nix/store/fjgnz0xfl04hsblsi4ym5y5akfh6mlmy-python3-3.8.5/lib/python3.8/site-packages:/nix/store/fjgnz0xfl04hsblsi4ym5y5akfh6mlmy-python3-3.8.5/lib/python3.8/site-packages $ PYTHONPATH=/somedir dontAddPythonPath=1 nix-shell ./shell-with-python3.nix --run 'echo $PYTHONPATH' /somedir
-
Shell hook may access not only the exported environment variables (which are currently cached by c-n-s) but also bash functions defined by setup hooks or
setup.sh
(which are not). In the following examplestripHash
is a bash function defined insetup.sh
.$ cat ./shell-print-stripHash.nix { pkgs ? import <nixpkgs> { } }: pkgs.mkShell { shellHook = "stripHash $NIX_CC"; } $ nix-shell ./shell-print-stripHash.nix --run : gcc-wrapper-9.3.0
If you need a workaround for this particular use case, you might add these lines to your bashrc:
if [ "$IN_CACHED_NIX_SHELL" ]; then
eval "$shellHook"
unset shellHook
fi
Or, if you'll need just the variable, you'll have to pass it with --keep
:
$ OTHERVAR=201 cached-nix-shell --keep OTHERVAR cached-shell.nix
Oh, I see, things are indeed more complicated than I thought. Thanks for explaining. The workaround you suggested works really well too. Thanks!
I wonder though if it wouldn't still be better/more expected to not cache shellHook and run it upon invocation. It certainly is what I expected, and from what I understand cached-nix-shell is already insensitive to environment variables which might alter what setuphook does (so your examples are with PYTHONPATH
would already be different with c-n-s, and like that they'd remain), so this change would be an improvement? Although of course, a drastic/possibly breaking change in behaviour.