Making sense of nix-shell

I’m trying to understand some apparently magic behavior of nix-shell. I have following nix expression which relates to a gem file, here with fake name gem_name:

  { lib, bundlerEnv, ruby }:                                                                                                     
  bundlerEnv rec {                                                                                                               
    name = "gem_name-${version}";                                                                                                
    version = (import gemset).gem_name.version;                                                                                  
    inherit ruby;                                                                                                                
    # expects Gemfile, Gemfile.lock and gemset.nix in the same directory                                                         
    gemdir = ./.;                                                                                                                                                                                                                                          

gemset.nix has been generated through bundix in the recommended way.

  1. nix-shell man page says it accepts a file with a nix expression defining a derivation. In fact, this file is not a derivation, but a function which if called with proper arguments defines a derivation. Reading at --arg argument documentation I have deduced that nix-shell has some kind of autocall behavior when the expression defines a function. How does it exactly work in this case, where --arg is not even provided? I haven’t found any documentation about it.

  2. If I run nix-shell I get an error:

error: undefined variable 'gemset' at /my/path/default.nix:6:21

However, if I run nix-shell -p <any_package>, for example, nix-shell -p hello it works. Reading at the docs, -p just makes a package available in the shell. However, it seems that it also makes the import to work.

  1. Which is the difference between nix-shell -p package and nix-shell -A attribute? Isn’t -A also used to load a package via its attribute?

Thanks for any insight

Edit: sorry, cutting out my use of numbers; I was numbering for my own benefit and not trying to map them to your questions; this is only a partial answer.

I’m not familiar with any of the nix+ruby ecosystem stuff, so I’m shooting from the hip a bit…

  • When you run nix-shell -p hello, it isn’t building your default.nix at all.
  • I think the reason you get the variable error is that nix is interpreting gemset as a variable and not a path type or string type. I think you want something like import ./gemset.nix
1 Like

generally import works on a path, you can think of it as copying and pasting all the text from that file into the expression. In general, the nix expression that’s imported is either a dictionary of configuration, or a function which given something like a version of nodejs is able to produce a package set.

Thanks for your answers @abathur & @jonringer.

About the import, it makes total sense. In fact, I found it weird having import gemset as I hadn’t seen anything like that in nix expression language documentation. However, I was just copying from nixpkgs documentation for ruby packages. Is it a typo there?

Changing gemset for the actual path makes nix-shell complain with:

error: cannot auto-call a function that has an argument without a default value ('lib')

But now I realize that, of course, that documentation expects the use of callPackage or something similar to build the derivation (so there is no autocall magic as I told), as it is documentation intended to build a package. I’ve updated it and now it works.

When you run nix-shell -p hello , it isn’t building your default.nix at all.

Oh!!! Thanks! I misunderstood the behavior of -p. I also realized that -A is completely different, as it selects an attribute from the current expression and creates the environment from it instead of from the whole expression.

Yes. In the example as written, import gemset doesn’t work. If you modify the example to include the attribute gemset = ./gemset.nix; (next to the definition of gemdir) then it would work, but you normally omit that definition because it has a default value based on gemdir already.