|
1 | | -{ config, lib, pkgs, ... }: |
| 1 | +{ config, lib, pkgs, options, ... }: |
2 | 2 | with lib; |
3 | 3 | let |
4 | 4 | cfg = config.devshell; |
|
18 | 18 | program = "${bin}"; |
19 | 19 | }; |
20 | 20 |
|
21 | | - mkSetupHook = entrypoint: |
| 21 | + mkSetupHook = rc: |
22 | 22 | pkgs.stdenvNoCC.mkDerivation { |
23 | 23 | name = "devshell-setup-hook"; |
24 | 24 | setupHook = pkgs.writeText "devshell-setup-hook.sh" '' |
25 | | - source ${devshell_dir}/env.bash |
| 25 | + source ${rc} |
26 | 26 | ''; |
27 | 27 | dontUnpack = true; |
28 | 28 | dontBuild = true; |
|
60 | 60 | envBash = pkgs.writeText "devshell-env.bash" '' |
61 | 61 | if [[ -n ''${IN_NIX_SHELL:-} || ''${DIRENV_IN_ENVRC:-} = 1 ]]; then |
62 | 62 | # We know that PWD is always the current directory in these contexts |
63 | | - export PRJ_ROOT=$PWD |
| 63 | + PRJ_ROOT=$PWD |
64 | 64 | elif [[ -z ''${PRJ_ROOT:-} ]]; then |
65 | | - echo "ERROR: please set the PRJ_ROOT env var to point to the project root" >&2 |
66 | | - return 1 |
| 65 | + ${lib.optionalString (cfg.prj_root_fallback != null) cfg.prj_root_fallback} |
| 66 | +
|
| 67 | + if [[ -z "''${PRJ_ROOT:-}" ]]; then |
| 68 | + echo "ERROR: please set the PRJ_ROOT env var to point to the project root" >&2 |
| 69 | + return 1 |
| 70 | + fi |
67 | 71 | fi |
68 | 72 |
|
| 73 | + export PRJ_ROOT |
| 74 | +
|
69 | 75 | # Expose the folder that contains the assembled environment. |
70 | 76 | export DEVSHELL_DIR=@DEVSHELL_DIR@ |
71 | 77 |
|
|
96 | 102 |
|
97 | 103 | # If the file is sourced, skip all of the rest and just source the env |
98 | 104 | # script. |
99 | | - if [[ $0 != "''${BASH_SOURCE[0]}" ]]; then |
| 105 | + if (return 0) &>/dev/null; then |
100 | 106 | source "$DEVSHELL_DIR/env.bash" |
101 | 107 | return |
102 | 108 | fi |
103 | 109 |
|
104 | 110 | # Be strict! |
105 | 111 | set -euo pipefail |
106 | 112 |
|
107 | | - if [[ $# = 0 ]]; then |
108 | | - # Start an interactive shell |
109 | | - exec "${bashPath}" --rcfile "$DEVSHELL_DIR/env.bash" --noprofile |
110 | | - elif [[ $1 == "-h" || $1 == "--help" ]]; then |
| 113 | + while (( "$#" > 0 )); do |
| 114 | + case "$1" in |
| 115 | + -h|--help) |
| 116 | + help=1 |
| 117 | + ;; |
| 118 | + --pure) |
| 119 | + pure=1 |
| 120 | + ;; |
| 121 | + --prj-root) |
| 122 | + if (( "$#" < 2 )); then |
| 123 | + echo 1>&2 '${cfg.name}: missing required argument to --prj-root' |
| 124 | + exit 1 |
| 125 | + fi |
| 126 | +
|
| 127 | + PRJ_ROOT="$2" |
| 128 | +
|
| 129 | + shift |
| 130 | + ;; |
| 131 | + --env-bin) |
| 132 | + if (( "$#" < 2 )); then |
| 133 | + echo 1>&2 '${cfg.name}: missing required argument to --env-bin' |
| 134 | + exit 1 |
| 135 | + fi |
| 136 | +
|
| 137 | + env_bin="$2" |
| 138 | +
|
| 139 | + shift |
| 140 | + ;; |
| 141 | + --) |
| 142 | + shift |
| 143 | + break |
| 144 | + ;; |
| 145 | + *) |
| 146 | + break |
| 147 | + ;; |
| 148 | + esac |
| 149 | +
|
| 150 | + shift |
| 151 | + done |
| 152 | +
|
| 153 | + if [[ -n "''${help:-}" ]]; then |
111 | 154 | cat <<USAGE |
112 | 155 | Usage: ${cfg.name} |
113 | 156 | $0 -h | --help # show this help |
114 | 157 | $0 [--pure] # start a bash sub-shell |
115 | 158 | $0 [--pure] <cmd> [...] # run a command in the environment |
116 | 159 |
|
117 | 160 | Options: |
118 | | - * --pure : execute the script in a clean environment |
| 161 | + * --pure : execute the script in a clean environment |
| 162 | + * --prj-root <path> : set the project root (\$PRJ_ROOT) |
| 163 | + * --env-bin <path> : path to the env executable (default: /usr/bin/env) |
119 | 164 | USAGE |
120 | 165 | exit |
121 | | - elif [[ $1 == "--pure" ]]; then |
122 | | - # re-execute the script in a clean environment |
123 | | - shift |
124 | | - exec /usr/bin/env -i -- "HOME=$HOME" "PRJ_ROOT=$PRJ_ROOT" "$0" "$@" |
| 166 | + fi |
| 167 | +
|
| 168 | + if (( "$#" == 0 )); then |
| 169 | + # Start an interactive shell |
| 170 | + set -- ${lib.escapeShellArg bashPath} --rcfile "$DEVSHELL_DIR/env.bash" --noprofile |
| 171 | + fi |
| 172 | +
|
| 173 | + if [[ -n "''${pure:-}" ]]; then |
| 174 | + # re-execute the script in a clean environment. |
| 175 | + # note that the `--` in between `"$0"` and `"$@"` will immediately |
| 176 | + # short-circuit options processing on the second pass through this |
| 177 | + # script, in case we get something like: |
| 178 | + # <entrypoint> --pure -- --pure <cmd> |
| 179 | + set -- "''${env_bin:-/usr/bin/env}" -i -- ''${HOME:+"HOME=''${HOME:-}"} ''${PRJ_ROOT:+"PRJ_ROOT=''${PRJ_ROOT:-}"} "$0" -- "$@" |
125 | 180 | else |
126 | 181 | # Start a script |
127 | 182 | source "$DEVSHELL_DIR/env.bash" |
128 | | - exec -- "$@" |
129 | 183 | fi |
| 184 | +
|
| 185 | + exec -- "$@" |
130 | 186 | ''; |
131 | 187 |
|
132 | 188 | # Builds the DEVSHELL_DIR with all the dependencies |
|
240 | 296 | type = types.package; |
241 | 297 | description = "TODO"; |
242 | 298 | }; |
| 299 | + |
| 300 | + prj_root_fallback = mkOption { |
| 301 | + type = let |
| 302 | + envType = options.env.type.nestedTypes.elemType; |
| 303 | + coerceFunc = value: { inherit value; }; |
| 304 | + in types.nullOr (types.coercedTo types.nonEmptyStr coerceFunc envType); |
| 305 | + apply = x: if x == null then x else x // { name = "PRJ_ROOT"; }; |
| 306 | + default = { eval = "$PWD"; }; |
| 307 | + example = lib.literalExpression '' |
| 308 | + { |
| 309 | + # Use the top-level directory of the working tree |
| 310 | + eval = "$(git rev-parse --show-toplevel)"; |
| 311 | + }; |
| 312 | + ''; |
| 313 | + description = '' |
| 314 | + If IN_NIX_SHELL is nonempty, or DIRENV_IN_ENVRC is set to '1', then |
| 315 | + PRJ_ROOT is set to the value of PWD. |
| 316 | +
|
| 317 | + This option specifies the path to use as the value of PRJ_ROOT in case |
| 318 | + IN_NIX_SHELL is empty or unset and DIRENV_IN_ENVRC is any value other |
| 319 | + than '1'. |
| 320 | +
|
| 321 | + Set this to null to force PRJ_ROOT to be defined at runtime (except if |
| 322 | + IN_NIX_SHELL or DIRENV_IN_ENVRC are defined as described above). |
| 323 | +
|
| 324 | + Otherwise, you can set this to a string representing the desired |
| 325 | + default path, or to a submodule of the same type valid in the 'env' |
| 326 | + options list (except that the 'name' field is ignored). |
| 327 | + ''; |
| 328 | + }; |
243 | 329 | }; |
244 | 330 |
|
245 | 331 | config.devshell = { |
|
313 | 399 | profile = cfg.package; |
314 | 400 | passthru = { |
315 | 401 | inherit config; |
316 | | - flakeApp = mkFlakeApp entrypoint; |
317 | | - hook = mkSetupHook entrypoint; |
| 402 | + flakeApp = mkFlakeApp "${devshell_dir}/entrypoint"; |
| 403 | + hook = mkSetupHook "${devshell_dir}/env.bash"; |
318 | 404 | inherit (config._module.args) pkgs; |
319 | 405 | }; |
320 | 406 | }; |
|
0 commit comments