How can I make a shell.nix that runs some code, only using inbuilt derivation function?

I basically want to create my own simple version of mkShell, so now I’m trying to understand how it would even be possible to execute some code before the shell actually pops up. I googled for a bit, and came up with this:

let pkgs = import <nixpkgs> {}; in
derivation {
  name = "hello";

  shellHook = ''
    echo "hello world!"
  '';

  system = builtins.currentSystem;

  builder = "${pkgs.bash}/bin/bash";
  args = [ ./setup.sh ];
}

From nix-shell documentation:

If the derivation defines the variable shellHook, it will be run after $stdenv/setup has been sourced. Since this hook is not executed by regular Nix builds, it allows you to perform initialisation specific to nix-shell. For example, the derivation attribute

shellHook =
  ''
    echo "Hello shell"
    export SOME_API_TOKEN="$(cat ~/.config/some-app/api-token)"
  '';

But that doesn’t output anything, although if I replace derivation {} with pkgs.mkShell {}, then it does. How can I make this behaviour work without actually using pkgs.mkShell?

This can’t work. Both nix-shell and pkgs.mkShell are magical in all of the bad ways, in my opinion. You’ll have to check the source code how exactly they are entangled; I recommend starting with mkShell.

Would be interesting to read what you find out. :slight_smile:

As far as I discovered, mkShell it not so big and uses mkDerivation under the hood. But I just can’t understand how mkDerivation works. Does it run some underlying native code? I just don’t know any other reason, this can’t be possible, not even the simplest similar version :frowning:

It’s not that magical. nix-shell runs shellHook, but it can only do so if the stdenv infrastructure for run hooks is in place. It’s the stdenv.mkDerivation function that establishes that. So if you use plain derivation, nix-shell skips the step. Replacing it with stdenv.mkDerivation works though.

If, uh, you really want to do so, you could probably roll some sort of minimal stdenv that creates only the interface that nix-shell expects, and pass that in to derivation. Relevant code starts here.

Ooh, I start getting it now, thank you very much!

1 Like