While digging deeper, I found a few interesting GitHub issues and PRs:
- "Lighter" and "cleaner" shell for `nix develop` and `nix shell` · Issue #4609 · NixOS/nix · GitHub
- Allow to get rid of `nix develop` "shell" logic · Issue #7501 · NixOS/nix · GitHub
- flakes: rename devShell to developPackages by Mic92 · Pull Request #3833 · NixOS/nix · GitHub
- How to get rid of mkNakedShell entirely · Issue #240 · cachix/devenv · GitHub
A recurring theme in these discussions is that there are two very different use cases at play:
I guess in general there are two major use cases:
- debug a build
- create a dev environment with tools suitable for developing on a project
nix develop comes with a bunch of assumptions - it’s designed to provide the build environment of a derivation.
That’s great when you want to poke at a package, rebuild quickly, or debug a derivation.
But those assumptions don’t necessarily match what we need for everyday development work, especially for projects that don’t produce Nix derivations.
And as @zimbatm noted in cachix/devenv#240, maybe nix develop should have been called nix debug, since it’s really about entering a build environment rather than setting up a developer workspace.
This made me wonder whether we’re stretching nix develop beyond what it was designed for - and if there’s any real reason to.
What exactly does Nix core need to do here that can’t be done entirely in userland (e.g. in nixpkgs)?
For debugging, sure - nix develop is convenient.
But for developer environments, I couldn’t find a reason this couldn’t live completely outside of core Nix.
This led me to experiment with a minimal userspace implementation that looks something like this:
pkgs: let
inherit (pkgs) lib;
mkRcFile = {
name,
shellHook,
packages,
}:
pkgs.writeTextFile {
name = "${name}-rc";
text = ''
[ -n "$PS1" ] && [ -e ~/.bashrc ] && source ~/.bashrc;
shopt -u expand_aliases
out='$PWD/outputs/out'
export NIX_BUILD_TOP="$(mktemp -d -t nix-shell.XXXXXX)"
${lib.toShellVar "nativeBuildInputs" packages}
. "${pkgs.lib.escapeShellArg pkgs.stdenvNoCC}/setup"
${shellHook}
shopt -s expand_aliases
'';
};
mkDeveloperShell = {
name ? "developer-shell",
packages ? [],
shellHook ? "",
bash ? pkgs.bashInteractive,
...
}:
pkgs.writeScriptBin name ''
#!${lib.getExe bash}
${pkgs.lib.getExe bash} --rcfile ${mkRcFile {inherit name shellHook packages;}}
'';
in
mkDeveloperShell {
shellHook = ''
echo $JAVA_HOME
exit
'';
packages = [
pkgs.jdk_headless
];
}
It gives the same power (including the option to use stdenv with setup hooks) but offers more flexibility - because it can evolve independently of Nix itself.
So here’s the question:
If developer environments can be expressed entirely in userspace, is using nix develop or nix-shell for that purpose an anti-pattern?