{ pkgs, lib ? pkgs.lib, config, ... }: let inherit (lib) mapAttrs mkOption types textClosureMap id attrNames; mkNakedShell = pkgs.callPackage ./../utils/mkNakedShell.nix {}; addAttributeName = prefix: mapAttrs ( k: v: v // { text = '' #### ${prefix}.${k} ${v.text} ''; } ); envBash = pkgs.writeText "devshell-env.bash" # sh '' export DEVSHELL_DIR=@DEVSHELL_DIR@ # nicely handle it at the front or between/end PATH=''${PATH#/path-not-set:} PATH=''${PATH#:/path-not-set} export PATH=$DEVSHELL_DIR/bin:$PATH ${textClosureMap id (addAttributeName "startup" config.enterShellCommands) (attrNames config.enterShellCommands)} # interactive session if [[ $- == *i* ]]; then true ${textClosureMap id (addAttributeName "interactive" config.interactiveShellCommands) (attrNames config.interactiveShellCommands)} fi # interactive session unset DEVSHELL_DIR ''; entryType = types.submodule { options = { text = mkOption { type = types.str; description = '' Script to run. ''; }; deps = mkOption { type = types.listOf types.str; default = []; description = '' A list of other steps that this one depends on. ''; }; }; }; in { options = { shell = { finalPackage = mkOption { type = types.package; internal = true; }; finalProfile = mkOption { type = types.package; internal = true; }; }; name = mkOption { type = types.str; default = "devshell"; description = '' Name of the shell. ''; }; packages = mkOption { type = types.listOf types.package; default = []; description = '' Packages to install into the devshell. ''; }; enterShellCommands = mkOption { type = types.attrsOf entryType; default = {}; description = '' Commands to run when entering the shell. ''; example = { "hello".text = "echo Hello world"; }; }; interactiveShellCommands = mkOption { type = types.attrsOf entryType; default = {}; description = '' Commands to run when entering an interactive shell. ''; example = { "hello".text = "echo Hello world"; }; }; }; config.shell = { finalProfile = pkgs.buildEnv { name = "${config.name}-profile"; paths = config.packages; postBuild = '' substitute ${envBash} $out/env.bash --subst-var-by DEVSHELL_DIR $out # NOTE: maybe also add an entrypoint shell script at $out/bin/devshell-${config.name}? ''; meta.mainProgram = "devshell-${config.name}"; }; finalPackage = mkNakedShell { name = config.name; profile = config.shell.finalProfile; }; }; }