diff --git a/modules/programs/direnv.nix b/modules/programs/direnv.nix index 951061f98..4d4862afb 100644 --- a/modules/programs/direnv.nix +++ b/modules/programs/direnv.nix @@ -130,46 +130,64 @@ in { ${getExe cfg.package} hook fish | source ''); - programs.nushell.extraConfig = mkIf cfg.enableNushellIntegration ( + programs.nushell.extraConfig = mkIf cfg.enableNushellIntegration (let + # We want to get the stdout from direnv even if it exits with non-zero, + # because it will have the DIRENV_ internal variables defined. + # + # However, nushell's current implementation of try-catch is subtly + # broken with external commands in pipelines[0]. + # + # This means we don't have a good way to ignore the exit code + # without using do | complete, which has a side effect of also + # capturing stderr, which we don't want. + # + # So, as a workaround, we wrap nushell in a second script that + # just ignores the exit code and does nothing else, allowing + # nushell to capture our stdout, but letting stderr go through + # and not causing a spurious "command failed" message. + # + # [0]: https://github.com/nushell/nushell/issues/13868 + # + # FIXME: remove the wrapper once the upstream issue is fixed + + direnvWrapped = pkgs.writeShellScript "direnv-wrapped" '' + ${getExe cfg.package} export json || true + ''; # Using mkAfter to make it more likely to appear after other # manipulations of the prompt. - mkAfter '' - $env.config = ($env.config? | default {}) - $env.config.hooks = ($env.config.hooks? | default {}) - $env.config.hooks.pre_prompt = ( - $env.config.hooks.pre_prompt? - | default [] - | append {|| - let direnv = ( - # We want to get the stdout from direnv even if it exits with non-zero, - # because it will have the DIRENV_ internal variables defined. - do --ignore-program-errors { ${ - getExe cfg.package - } export json } - | from json --strict - | default {} - ) - if ($direnv | is-empty) { - return - } - $direnv - | items {|key, value| - { - key: $key - value: (do ( - $env.ENV_CONVERSIONS? - | default {} - | get -i $key - | get -i from_string - | default {|x| $x} - ) $value) - } - } - | transpose -ird - | load-env - } - ) - ''); + in mkAfter '' + $env.config = ($env.config? | default {}) + $env.config.hooks = ($env.config.hooks? | default {}) + $env.config.hooks.pre_prompt = ( + $env.config.hooks.pre_prompt? + | default [] + | append {|| + let direnv = ( + ${direnvWrapped} + | from json --strict + | default {} + ) + if ($direnv | is-empty) { + return + } + $direnv + | items {|key, value| + { + key: $key + value: (do ( + $env.ENV_CONVERSIONS? + | default {} + | get -i $key + | get -i from_string + | default {|x| $x} + ) $value) + } + } + | transpose -ird + | load-env + } + ) + ''); home.sessionVariables = lib.mkIf cfg.silent { DIRENV_LOG_FORMAT = ""; }; }; diff --git a/tests/modules/programs/direnv/nushell.nix b/tests/modules/programs/direnv/nushell.nix index 502b325d5..86293e1d7 100644 --- a/tests/modules/programs/direnv/nushell.nix +++ b/tests/modules/programs/direnv/nushell.nix @@ -13,6 +13,6 @@ "home-files/.config/nushell/config.nu"; in '' assertFileExists "${configFile}" - assertFileRegex "${configFile}" '/nix/store/.*direnv.*/bin/direnv export json' + assertFileRegex "${configFile}" '/nix/store/.*direnv-wrapped' ''; }