A modular almost-drop-in replacement for the mkShell ("em-kay shell") function.
I can't define nix modules any better than nix.dev's excellent quick introduction:
- A module is a function that takes an attribute set and returns an attribute set.
- It may declare options, telling which attributes are allowed in the final outcome.
- It may define values, for options declared by itself or other modules.
- When evaluated by the module system, it produces an attribute set based on the declarations and definitions.
make-shell
evaluates its argument as a module, using lib.evalModules
from nixpkgs, modifying a few things to match the behavior of mkShell
, passes the resulting configuration to mkDerivation
, and returns the result: a derivation suitable for use as a shell environment with nix develop
.
If you're wondering whether you can benefit from this, check this repository's WHY file.
This repository is a nix flake with an overlay output that adds make-shell to a nixpkgs set. It also has a flake module output which can be used with flake-parts, which lets you write your flakes using modules!). You can use either to make make-shell
available to your flake.
If you aren't using flakes you can still import this repository using tools like npins or niv.
Either way, start by adding this flake to the inputs of your flake:
inputs.make-shell.url = "github:nicknovitski/make-shell";
Then, to define a devShells.default
flake output with the overlay:
outputs = {
nixpkgs,
flake-utils,
make-shell,
...,
}:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {
inherit system;
config = {};
overlays = [make-shell.overlays.default];
};
in {
devShells.default = pkgs.make-shell {
packages = [pkgs.curl pkgs.git pkgs.jq pkgs.wget];
};
});
To do the same thing with the flake module instead:
outputs = inputs@{
nixpkgs,
flake-parts,
make-shell,
...,
}:
flake-parts.lib.mkFlake {inherit inputs;} (_: {
imports = [make-shell.flakeModules.default];
systems = [ "aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux" ];
perSystem = {...}: {
make-shells.default = {pkgs, ...}: {
packages = [pkgs.curl pkgs.git pkgs.jq pkgs.wget];
};
};
});
make-shell
has one parameter: a shell module. A shell module is either an attribute set of shell options, or a function with an attribute set parameter which returns an attribute set of shell options. The parameters and options are documented in SHELL_MODULES.md.
If you're using flake-parts, the flake module options, which include all the shell module options, are also documented on the flake.parts site.
All common attributes used with mkShell
are also valid Shell Options! That means that make-shell
can often be a drop-in replacement for mkShell
. When it isn't, there's only two possible changes you need to make:
Arguments which are intended to be environment variables in the shell environment should be changed to attributes of the env
option
For example:
pkgs.mkShell { GRADLE_OPTS = "-Dorg.gradle.appname="; } == pkgs.make-shell { env.GRADLE_OPTS = "-Dorg.gradle.appname="; }
Arbitrary mkDerivation arguments should be changed to be attributes of the additionalArguments
option
For example:
let
args = {
doCheck = true;
phases = [ "buildPhase" "checkPhase" ];
checkPhase = "echo seems fine!";
};
in pkgs.mkShell args == pkgs.make-shell { additionalArguments = args; }