Nix-shell-locked: tool that starts transient shell with temporary packages that reads flake.lock to determine nixpkgs version

Hi! Introducing nix-shell-locked which is a tool for starting temporary shells with specified packages available similar to nix-shell -p or nix shell except that you can point it at a flake.lock file and it will read the revision of nixpkgs and then install packages from that revision. Typically one would point it to the flake.lock file from a home-manager or system wide config managed with flakes.

I manage my system with flakes and I found that trying out new packages in a nix-shell -p <package> shell would sometimes lead to runtime errors when my profile’s channel was out of sync with the version of nixpkgs associated with the system config.

3 Likes

Neat! Out of curiosity, how does this differ from setting nix.nixPath when declaring your configuration? For example, I do this:

# Ensure that commands like `nix repl` and `nix-shell` have access to the
# same nixpkgs we use to install everything else.
nix.nixPath = [ "nixpkgs=${pkgs.path}" ];
2 Likes

I didn’t know about that feature of nix when I made this. Thanks for sharing! One difference is you can point it at an arbitrary flake.lock file, so on non-NixOS systems that use home-manager+flakes you can easily enter a shell with packages from a matching nixpkgs.

But when I shared this a part of me was secretly hoping that someone would teach me a new thing about NixOS that would make this tool mostly unnecessary :sweat_smile:.

1 Like

@jfly I tried out setting nix.nixPath = [ "nixpkgs=${pkgs.path}" ]; in my configuration.nix file but it didn’t do what I expected.

As an experiment, I haven’t updated my channels in a while and when I install some graphical programs with nix-shell -p I get runtime errors because the system graphics libraries installed with flakes and kept up to date are not compatible with the older version of the graphical programs. E.g.

 $ nix-shell -p sameboy --command sameboy
SameBoy v0.15.8
Couldn't find matching GLX visual

After setting nix.nixPath as in your example I get the same error, but when I install sameboy using nix shell it works fine. My expectation was that setting nixPath would somehow make it so that programs installed with nix-env and nix-shell would use the same version of nixpkgs as was used to install the system configuration (ie. the version corresponding to the revision in my system flake.lock file).

My configuration looks quite different from yours though so I suppose it’s possible I’m setting the wrong thing. Why does your solution work for you? How does the nixPath attribute affect how nix expressions are evaluated by commands like nix-shell? Any advice for debugging this or links to documentation about what setting nixPath is doing behind the scenes?

The plot thickens! It looks like the effect of adding nix.nixPath = [ "nixpkgs=${pkgs.path}" ]; was to update the value of “nixpkgs” in my default $NIX_PATH environment variable. The full value of $NIX_PATH is:
/home/s/.nix-defexpr/channels:nixpkgs=/nix/store/q6kh2iz0nzm5mpyijqki57q38llscczi-s9yan4kpplycs6i3iplair8q74vz5r5s-source:/home/s/.nix-defexpr/channels:/nix/var/nix/profiles/per-user/root/channels. If I manually change it to just contain nixpkgs=/nix/store/q6kh2iz0nzm5mpyijqki57q38llscczi-s9yan4kpplycs6i3iplair8q74vz5r5s-source, running nix-shell -p sameboy --command sameboy works!

I found that I was adding a bunch of crud to NIX_PATH in my .profile and I removed that so now it just contains /home/s/.nix-defexpr/channels:nixpkgs=/nix/store/q6kh2iz0nzm5mpyijqki57q38llscczi-s9yan4kpplycs6i3iplair8q74vz5r5s-source. The first entry in that variable is causing channels to take precedence over the custom entry. There doesn’t seem to be an elegant way to remove the first entry (I could of course just set NIX_PATH directly in a shell rc file but that seems messy) so instead I just removed all my channels, and now nix-shell -p is installing packages from the same nixpkgs version as the system.

Interesting. My NIX_PATH is simpler:

$ echo $NIX_PATH
nixpkgs=/nix/store/szkschp8grgahp35bg2pqwqmglkq52v5-wsci3as2flp84w6w5gzbyphxzf6ilasd-source

Reading the nixpkgs code a bit, it looks like nixPath turns into a value in environment.sessionVariables, and environment.sessionVariables ends up in /etc/pam/environment. What do you find if you look for NIX_PATH in that file? For example:

$ grep NIX_PATH /etc/pam/environment
NIX_PATH   DEFAULT="nixpkgs=/nix/store/szkschp8grgahp35bg2pqwqmglkq52v5-wsci3as2flp84w6w5gzbyphxzf6ilasd-source"

Or maybe you’ve got something in your shell configuration somewhere that modifies the NIX_PATH env var?

Oh interesting, there’s no mention of channels at all in your NIX_PATH. Does that mean if you wanted to use channels you couldn’t?

I temporarily removed my .profile and .bashrc and I still have the channels prefix in my NIX_PATH. I traced the problem to /etc/profile which includes:

if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
    . /nix/store/3cxmjamx4qs16syczzvqkphps51dwrmz-set-environment
fi

…and the set-environment script is doing:

if [ -e "$HOME/.nix-defexpr/channels" ]; then
  export NIX_PATH="$HOME/.nix-defexpr/channels${NIX_PATH:+:$NIX_PATH}"
fi

@jfly did you remove your ~/.nix-defexpr/channels symlink? I tried doing that and now my NIX_PATH looks like yours.

Besides using something that sets NIX_PATH, there’s also the --inputs-from option, thought it applies only to nix shell not nix-shell. If you run nix shell --inputs-from=some/flake/path nixpkgs#some_package it will use the version of nixpkgs locked in that flake. Of course it can be used to address any other inputs of the referenced flake too.

You might be tempted to use the nix.registry option to set the nixpkgs flake reference to that used in the home-manager configuration but I strongly caution against it. Doing so means that when you would use nix flake lock --update-input=nixpkgs it will change the nixpkgs of that flake to the home configuration input, including whenever you might try to update the nixpkgs input for your home configuration.

2 Likes

@gridbugs the symlinks in ~/.nix-defexpr are broken symlinks for me. I’m not sure why… I don’t think I did anything special to make this so:

$ ls -alh ~/.nix-defexpr/
total 8.0K
drwxr-xr-x  2 jeremy users 4.0K Oct 22  2022 .
drwx------ 53 jeremy users 4.0K May  6 22:10 ..
lrwxrwxrwx  1 jeremy users   46 Oct 22  2022 channels -> /nix/var/nix/profiles/per-user/jeremy/channels
lrwxrwxrwx  1 jeremy users   44 Oct 22  2022 channels_root -> /nix/var/nix/profiles/per-user/root/channels