|
1 | | -{ lib |
2 | | -, bashInteractive |
3 | | -, buildEnv |
4 | | -, coreutils |
5 | | -, pkgs |
6 | | -, system |
7 | | -, writeText |
8 | | -, writeTextFile |
9 | | -, writeShellScriptBin |
| 1 | +{ nixpkgs ? <nixpkgs> |
| 2 | +, system ? builtins.currentSystem |
10 | 3 | }: |
11 | | -let |
12 | | - bashBin = "${bashInteractive}/bin"; |
13 | | - bashPath = "${bashInteractive}/bin/bash"; |
14 | | - |
15 | | - # Transform the env vars into bash exports |
16 | | - envToBash = env: |
17 | | - builtins.concatStringsSep "\n" |
18 | | - (lib.mapAttrsToList |
19 | | - (k: v: "export ${k}=${lib.escapeShellArg (toString v)}") |
20 | | - env |
21 | | - ) |
22 | | - ; |
23 | | - |
24 | | - # A developer shell that works in all scenarios |
25 | | - # |
26 | | - # * nix-build |
27 | | - # * nix-shell |
28 | | - # * flake app |
29 | | - # * direnv integration |
30 | | - mkDevShell = module: |
31 | | - let |
32 | | - config = (lib.evalModules { |
33 | | - modules = [ ./options.nix module ]; |
34 | | - args = { |
35 | | - inherit pkgs; |
36 | | - }; |
37 | | - }).config; |
38 | | - |
39 | | - inherit (config) |
40 | | - bash |
41 | | - commands |
42 | | - env |
43 | | - motd |
44 | | - name |
45 | | - packages |
46 | | - ; |
47 | | - |
48 | | - envDrv = buildEnv { |
49 | | - # TODO: support passing more arguments here |
50 | | - name = "${name}-env"; |
51 | | - paths = |
52 | | - let |
53 | | - op = |
54 | | - { name, command, ... }: |
55 | | - if command == null || command == "" then [ ] |
56 | | - else [ |
57 | | - (writeShellScriptBin name (toString command)) |
58 | | - ]; |
59 | | - in |
60 | | - (builtins.concatMap op commands) ++ packages; |
61 | | - }; |
62 | | - |
63 | | - # write a bash profile to load |
64 | | - bashrc = writeText "${name}-bashrc" '' |
65 | | - # Set all the passed environment variables |
66 | | - ${envToBash env} |
67 | | -
|
68 | | - # Prepend the PATH with the devshell dir and bash |
69 | | - PATH=''${PATH#/path-not-set:} |
70 | | - PATH=''${PATH#${bashBin}:} |
71 | | - export PATH=$DEVSHELL_DIR/bin:${bashBin}:$PATH |
72 | | -
|
73 | | - # Expose the path to nixpkgs |
74 | | - export NIXPKGS_PATH=${toString pkgs.path} |
75 | | -
|
76 | | - # Load installed profiles |
77 | | - for file in "$DEVSHELL_DIR/etc/profile.d/"*.sh; do |
78 | | - # If that folder doesn't exist, bash loves to return the whole glob |
79 | | - [[ -f "$file" ]] && source "$file" |
80 | | - done |
81 | | -
|
82 | | - # Use this to set even more things with bash |
83 | | - ${bash.extra or ""} |
84 | | -
|
85 | | - __devshell-motd() { |
86 | | - cat <<DEVSHELL_PROMPT |
87 | | - ${motd} |
88 | | - DEVSHELL_PROMPT |
89 | | - } |
90 | | -
|
91 | | - # Print the motd in direnv |
92 | | - if [[ ''${DIRENV_IN_ENVRC:-} = 1 ]]; then |
93 | | - __devshell-motd |
94 | | - fi |
95 | | -
|
96 | | - # Interactive sessions |
97 | | - if [[ $- == *i* ]]; then |
98 | | -
|
99 | | - # Print information if the prompt is every displayed. We have to make |
100 | | - # that distinction because `nix-shell -c "cmd"` is running in |
101 | | - # interactive mode. |
102 | | - __devshell-prompt() { |
103 | | - __devshell-motd |
104 | | - # Make it a noop |
105 | | - __devshell-prompt() { :; } |
106 | | - } |
107 | | - PROMPT_COMMAND=__devshell-prompt''${PROMPT_COMMAND+;$PROMPT_COMMAND} |
108 | | -
|
109 | | - # Set a cool PS1 |
110 | | - if [[ -n "$PS1" ]]; then |
111 | | - # Print the path relative to $DEVSHELL_ROOT |
112 | | - rel_root() { |
113 | | - local path |
114 | | - path=$(${coreutils}/bin/realpath --relative-to "$DEVSHELL_ROOT" "$PWD") |
115 | | - if [[ $path != . ]]; then |
116 | | - echo " $path " |
117 | | - fi |
118 | | - } |
119 | | - PS1='\[\033[38;5;202m\][${name}]$(rel_root)\$\[\033[0m\] ' |
120 | | - fi |
121 | | -
|
122 | | - # Load bash completions |
123 | | - for file in "$DEVSHELL_DIR/share/bash-completion/completions/"* ; do |
124 | | - [[ -f "$file" ]] && source "$file" |
125 | | - done |
126 | | -
|
127 | | - ${bash.interactive or ""} |
128 | | -
|
129 | | - fi # Interactive session |
130 | | - ''; |
131 | | - |
132 | | - # This is our entry-point for everything! |
133 | | - devShellBin = derivation { |
134 | | - inherit system; |
135 | | - name = "${name}-bin"; |
136 | | - |
137 | | - # Define our own minimal builder. |
138 | | - builder = bashPath; |
139 | | - args = [ |
140 | | - "-ec" |
141 | | - '' |
142 | | - ${coreutils}/bin/cp $envScriptPath $out && |
143 | | - ${coreutils}/bin/chmod +x $out; |
144 | | - exit 0 |
145 | | - '' |
146 | | - ]; |
147 | | - |
148 | | - # The actual devshell wrapper script |
149 | | - envScript = '' |
150 | | - #!${bashPath} |
151 | | - # Script that sets-up the environment. Can be both sourced or invoked. |
152 | | -
|
153 | | - # This is the directory that contains our dependencies |
154 | | - export DEVSHELL_DIR=${envDrv} |
155 | | - # It assums that the shell is always loaded from the root of the project |
156 | | - # Store that for later usage. |
157 | | - export DEVSHELL_ROOT=$PWD |
158 | | -
|
159 | | - # If the file is sourced, skip all of the rest and just source the |
160 | | - # bashrc |
161 | | - if [[ $0 != "''${BASH_SOURCE[0]}" ]]; then |
162 | | - source "${bashrc}" |
163 | | - return |
164 | | - fi |
165 | | -
|
166 | | - # Be strict! |
167 | | - set -euo pipefail |
168 | | -
|
169 | | - if [[ $# = 0 ]]; then |
170 | | - # Start an interactive shell |
171 | | - exec "${bashPath}" --rcfile "${bashrc}" --noprofile |
172 | | - elif [[ $1 == "-h" || $1 == "--help" ]]; then |
173 | | - cat <<USAGE |
174 | | - Usage: ${name} |
175 | | - source $0 # load the environment in the current bash |
176 | | - $0 -h | --help # show this help |
177 | | - $0 [--pure] # start a bash sub-shell |
178 | | - $0 [--pure] <cmd> [...] # run a command in the environment |
179 | | -
|
180 | | - Options: |
181 | | - * --pure : execute the script in a clean environment |
182 | | - USAGE |
183 | | - exit |
184 | | - elif [[ $1 == "--pure" ]]; then |
185 | | - # re-execute the script in a clean environment |
186 | | - shift |
187 | | - exec -c "$0" "$@" |
188 | | - else |
189 | | - # Start a script |
190 | | - source "${bashrc}" |
191 | | - exec -- "$@" |
192 | | - fi |
193 | | - ''; |
194 | | - |
195 | | - passAsFile = [ "envScript" ]; |
196 | | - }; |
197 | | - |
198 | | - # Use this to define a flake app for the environment. |
199 | | - flakeApp = { |
200 | | - type = "app"; |
201 | | - program = "${devShellBin}"; |
202 | | - }; |
203 | | - |
204 | | - # Use a naked derivation to limit the amount of noise passed to nix-shell. |
205 | | - devShell = derivation { |
206 | | - inherit name system; |
207 | | - |
208 | | - # `nix develop` actually checks and uses builder. And it must be bash. |
209 | | - builder = bashPath; |
210 | | - # Bring in the dependencies on `nix-build` |
211 | | - args = [ "-ec" "${coreutils}/bin/ln -s ${devShellBin} $out; exit 0" ]; |
212 | | - |
213 | | - # $stdenv/setup is loaded by nix-shell during startup. |
214 | | - # https://github.com/nixos/nix/blob/377345e26f1ac4bbc87bb21debcc52a1d03230aa/src/nix-build/nix-build.cc#L429-L432 |
215 | | - stdenv = writeTextFile { |
216 | | - name = "devshell-stdenv"; |
217 | | - destination = "/setup"; |
218 | | - text = '' |
219 | | - # Fix for `nix develop` |
220 | | - : ''${outputs:=out} |
221 | | -
|
222 | | - runHook() { |
223 | | - eval "$shellHook" |
224 | | - unset runHook |
225 | | - } |
226 | | - ''; |
227 | | - }; |
228 | | - |
229 | | - # The shellHook is loaded directly by `nix develop`. But nix-shell |
230 | | - # requires that other trampoline. |
231 | | - shellHook = '' |
232 | | - # Remove all the unnecessary noise that is set by the build env |
233 | | - unset NIX_BUILD_TOP NIX_BUILD_CORES NIX_BUILD_TOP NIX_STORE |
234 | | - unset TEMP TEMPDIR TMP TMPDIR |
235 | | - unset builder name out shellHook stdenv system |
236 | | - # Flakes stuff |
237 | | - unset dontAddDisableDepTrack outputs |
238 | | -
|
239 | | - # For `nix develop` |
240 | | - if [[ "$SHELL" == "/noshell" ]]; then |
241 | | - export SHELL=${bashPath} |
242 | | - fi |
243 | | -
|
244 | | - # Load the dev shell environment |
245 | | - source "${devShellBin}" |
246 | | - ''; |
247 | | - }; |
248 | | - |
249 | | - out = devShell // { |
250 | | - inherit flakeApp; |
251 | | - }; |
252 | | - in |
253 | | - out |
254 | | - ; |
255 | | - |
256 | | - # Build the devshell from pure JSON-like data |
257 | | - fromData = data: mkDevShell data; |
258 | | - |
259 | | - importTOML = path: builtins.fromTOML (builtins.readFile path); |
260 | | - |
261 | | - # Build the devshell from a TOML declaration |
262 | | - fromTOML = path: fromData (importTOML path); |
263 | | -in |
264 | | -{ |
265 | | - inherit |
266 | | - fromData |
267 | | - fromTOML |
268 | | - importTOML |
269 | | - mkDevShell |
270 | | - ; |
271 | | - |
272 | | - __functor = _: mkDevShell; |
| 4 | +import nixpkgs { |
| 5 | + inherit system; |
| 6 | + overlays = [ (import ./overlay.nix) ]; |
273 | 7 | } |
0 commit comments