1# This module defines a global environment configuration and
2# a common configuration for all shells.
3
4{ config, lib, utils, pkgs, ... }:
5
6with lib;
7
8let
9
10 cfg = config.environment;
11
12 exportedEnvVars =
13 let
14 absoluteVariables =
15 mapAttrs (n: toList) cfg.variables;
16
17 suffixedVariables =
18 flip mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
19 concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
20 );
21
22 allVariables =
23 zipAttrsWith (n: concatLists) [ absoluteVariables suffixedVariables ];
24
25 exportVariables =
26 mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'') allVariables;
27 in
28 concatStringsSep "\n" exportVariables;
29in
30
31{
32
33 options = {
34
35 environment.variables = mkOption {
36 default = {};
37 example = { EDITOR = "nvim"; VISUAL = "nvim"; };
38 description = lib.mdDoc ''
39 A set of environment variables used in the global environment.
40 These variables will be set on shell initialisation (e.g. in /etc/profile).
41 The value of each variable can be either a string or a list of
42 strings. The latter is concatenated, interspersed with colon
43 characters.
44 '';
45 type = with types; attrsOf (either str (listOf str));
46 apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
47 };
48
49 environment.profiles = mkOption {
50 default = [];
51 description = lib.mdDoc ''
52 A list of profiles used to setup the global environment.
53 '';
54 type = types.listOf types.str;
55 };
56
57 environment.profileRelativeEnvVars = mkOption {
58 type = types.attrsOf (types.listOf types.str);
59 example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
60 description = lib.mdDoc ''
61 Attribute set of environment variable. Each attribute maps to a list
62 of relative paths. Each relative path is appended to the each profile
63 of {option}`environment.profiles` to form the content of the
64 corresponding environment variable.
65 '';
66 };
67
68 # !!! isn't there a better way?
69 environment.extraInit = mkOption {
70 default = "";
71 description = lib.mdDoc ''
72 Shell script code called during global environment initialisation
73 after all variables and profileVariables have been set.
74 This code is assumed to be shell-independent, which means you should
75 stick to pure sh without sh word split.
76 '';
77 type = types.lines;
78 };
79
80 environment.shellInit = mkOption {
81 default = "";
82 description = lib.mdDoc ''
83 Shell script code called during shell initialisation.
84 This code is assumed to be shell-independent, which means you should
85 stick to pure sh without sh word split.
86 '';
87 type = types.lines;
88 };
89
90 environment.loginShellInit = mkOption {
91 default = "";
92 description = lib.mdDoc ''
93 Shell script code called during login shell initialisation.
94 This code is assumed to be shell-independent, which means you should
95 stick to pure sh without sh word split.
96 '';
97 type = types.lines;
98 };
99
100 environment.interactiveShellInit = mkOption {
101 default = "";
102 description = lib.mdDoc ''
103 Shell script code called during interactive shell initialisation.
104 This code is assumed to be shell-independent, which means you should
105 stick to pure sh without sh word split.
106 '';
107 type = types.lines;
108 };
109
110 environment.shellAliases = mkOption {
111 example = { l = null; ll = "ls -l"; };
112 description = lib.mdDoc ''
113 An attribute set that maps aliases (the top level attribute names in
114 this option) to command strings or directly to build outputs. The
115 aliases are added to all users' shells.
116 Aliases mapped to `null` are ignored.
117 '';
118 type = with types; attrsOf (nullOr (either str path));
119 };
120
121 environment.homeBinInPath = mkOption {
122 description = lib.mdDoc ''
123 Include ~/bin/ in $PATH.
124 '';
125 default = false;
126 type = types.bool;
127 };
128
129 environment.localBinInPath = mkOption {
130 description = lib.mdDoc ''
131 Add ~/.local/bin/ to $PATH
132 '';
133 default = false;
134 type = types.bool;
135 };
136
137 environment.binsh = mkOption {
138 default = "${config.system.build.binsh}/bin/sh";
139 defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"'';
140 example = literalExpression ''"''${pkgs.dash}/bin/dash"'';
141 type = types.path;
142 visible = false;
143 description = lib.mdDoc ''
144 The shell executable that is linked system-wide to
145 `/bin/sh`. Please note that NixOS assumes all
146 over the place that shell to be Bash, so override the default
147 setting only if you know exactly what you're doing.
148 '';
149 };
150
151 environment.shells = mkOption {
152 default = [];
153 example = literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
154 description = lib.mdDoc ''
155 A list of permissible login shells for user accounts.
156 No need to mention `/bin/sh`
157 here, it is placed into this list implicitly.
158 '';
159 type = types.listOf (types.either types.shellPackage types.path);
160 };
161
162 };
163
164 config = {
165
166 system.build.binsh = pkgs.bashInteractive;
167
168 # Set session variables in the shell as well. This is usually
169 # unnecessary, but it allows changes to session variables to take
170 # effect without restarting the session (e.g. by opening a new
171 # terminal instead of logging out of X11).
172 environment.variables = config.environment.sessionVariables;
173
174 environment.profileRelativeEnvVars = config.environment.profileRelativeSessionVariables;
175
176 environment.shellAliases = mapAttrs (name: mkDefault) {
177 ls = "ls --color=tty";
178 ll = "ls -l";
179 l = "ls -alh";
180 };
181
182 environment.etc.shells.text =
183 ''
184 ${concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
185 /bin/sh
186 '';
187
188 # For resetting environment with `. /etc/set-environment` when needed
189 # and discoverability (see motivation of #30418).
190 environment.etc.set-environment.source = config.system.build.setEnvironment;
191
192 system.build.setEnvironment = pkgs.writeText "set-environment"
193 ''
194 # DO NOT EDIT -- this file has been generated automatically.
195
196 # Prevent this file from being sourced by child shells.
197 export __NIXOS_SET_ENVIRONMENT_DONE=1
198
199 ${exportedEnvVars}
200
201 ${cfg.extraInit}
202
203 ${optionalString cfg.homeBinInPath ''
204 # ~/bin if it exists overrides other bin directories.
205 export PATH="$HOME/bin:$PATH"
206 ''}
207
208 ${optionalString cfg.localBinInPath ''
209 export PATH="$HOME/.local/bin:$PATH"
210 ''}
211 '';
212
213 system.activationScripts.binsh = stringAfter [ "stdio" ]
214 ''
215 # Create the required /bin/sh symlink; otherwise lots of things
216 # (notably the system() function) won't work.
217 mkdir -m 0755 -p /bin
218 ln -sfn "${cfg.binsh}" /bin/.sh.tmp
219 mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
220 '';
221
222 };
223
224}