devshell/lib/modules/main.nix
2025-07-31 12:37:19 +02:00

113 lines
2.5 KiB
Nix

{
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";
};
packages = mkOption {
type = types.listOf types.package;
default = [];
};
enterShellCommands = mkOption {
type = types.attrsOf entryType;
default = {};
};
interactiveShellCommands = mkOption {
type = types.attrsOf entryType;
default = {};
};
};
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;
};
};
}