Search code examples
nixnix-shell

Non-interactive nix-shell


I'm trying to write a shell.nix to have a stable environment for doing web development in a project. I'd like to use this so that I don't have to write nix-shell -p nodejs_22 --command "yarn dev". However, I don't see a way to specify this in the shell.nix file.

Here's what I came up with:

{ pkgs ? import <nixpkgs> {} }:

let lib = import <nixpkgs/lib>;
in pkgs.mkShell {
  packages = with pkgs; [
    nodejs_22 yarn
  ];

  shellHook = ''
    export PATH="${pkgs.nodejs_22.out}/bin:$PATH"
    yarn dev
  '';
}

The problem here is that shellHook is not really meant to do this. It's meant for initialization purposes. When I CTRL+C (SIGINT) on the node process, it drops me into bash, inside the nix shell. However, I want it to just exit entirely and go back to the shell before nix-shell was run.

How can I specify the --command argument inside the shell.nix file? It it's not possible, it's kind of pointless to me since I could just create a shell script with the full nix-shell command instead.

Using trap - SIGINT, trap "exit 130" INT and variations (including using job control) did not help.


Solution

  • As you've discovered, nix-shell isn't really meant for this. If you really insist on this way of running your dev command, then you need an exec to replace the bash process running your shellHook with yarn itself:

    { pkgs ? import <nixpkgs> {} }:
    pkgs.mkShell {
      buildInputs = [
        pkgs.nodejs_22
        pkgs.yarn
      ];
    
      shellHook = "exec yarn dev";
    }
    

    This way, a Ctrl+C should not drop you into the shell inside nix-shell but directly exit.