makeSetupHook and shell.nix

I just discovered makeSetupHook and an interesting use for it in shell.nix files.

In a few projects, I had shell.nix files that used shellHook to run helper scripts I’d tucked away in a ./nix subdirectory.

For example, in a few cases I wanted to spin up a database upon entering the shell and spin it down upon exiting:

{ pkgs ? import <nixpkgs> {} }:

with pkgs; mkShell {
  name = "foo";
  buildInputs = [ postgresql ];
  shellHook = ''
    trap "'$PWD/nix/pgSetup' remove" EXIT
    nix/pgSetup
  '';
}

One problem with this approach is it relies on the presence of these random shell scripts that are not represented as part of the derivation itself. Another problem is that I end up duplicating these scripts across projects.

I was going to address this by packing the helper scripts with writeShellScript, but then I noticed makeSetupHook. It sounds like it was designed with a different use-case in mind, but it lets me package the setup script and automatically runs it upon entering the shell as long as I include it in my buildInputs:

{ pkgs ? import <nixpkgs> {} }:

let
  pgSetup = with pkgs; makeSetupHook { } ./nix/pgSetup;

in with pkgs; mkShell {
  name = "foo";
  buildInputs = [ postgresql pgSetup ];
  shellHook = ''
    trap '${pgSetup}/nix-support/setup-hook remove' EXIT
  '';
}

So ./nix/pgSetup needs to exist relative to the derivation, but it doesn’t need to exist relative to nix-shell invocation, allowing, for example, using it like:

nix-shell https://github.com/path/to/nix-shells/archive/master.tar.gz -A foo

I might actually switch to using writeShellScript, as it offers more flexibility and control, but I thought this was interesting.

4 Likes