Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/release-notes/rl-2611.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ This release has the following notable changes:
domain to `user` for agents that should run without an active graphical login
session.

- [](#opt-programs.nushell.enable) now loads [](#opt-home.sessionVariables)
when Nushell starts directly. When Home Manager is used as a NixOS or
nix-darwin module, it also loads the system environment. This makes its
environment consistent with managed Bash and Zsh sessions. If you manage
Nushell's `env.nu` independently, migrate its contents to
[](#opt-programs.nushell.envFile) or [](#opt-programs.nushell.extraEnv).

## State Version Changes {#sec-release-26.11-state-version-changes}

The state version in this release includes the changes below. These
Expand Down
100 changes: 99 additions & 1 deletion modules/programs/nushell.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
config,
lib,
osConfig,
pkgs,
...
}:
Expand All @@ -9,6 +10,102 @@ let
inherit (lib.hm.nushell) isNushellInline toNushell;
cfg = config.programs.nushell;

systemEnvironmentPath = [
"system"
"build"
"setEnvironment"
];
effectiveOsConfig = if osConfig == null then { } else osConfig;
getSystemEnvironment = lib.attrByPath systemEnvironmentPath null;
systemEnvironmentFile = getSystemEnvironment effectiveOsConfig;
hasSystemEnvironment = systemEnvironmentFile != null;
sessionVariablesPackage = toString config.home.sessionVariablesPackage;
sessionVariablesFile = "/etc/profile.d/hm-session-vars.sh";
homeSessionVariablesFile = sessionVariablesPackage + sessionVariablesFile;
hasSessionEnvironment =
systemEnvironmentFile != null
|| config.home.sessionVariables != { }
|| config.home.sessionSearchVariables != { }
|| config.home.sessionVariablesExtra != "";
hasUserEnvFile = cfg.envFile != null || cfg.extraEnv != "";
writeEnvFile = hasSessionEnvironment || hasUserEnvFile;

# Translate the POSIX session scripts at shell startup so references to
# runtime values such as HOME and USER are expanded in the user environment.
sessionVariablesLoader =
let
sourceSystemEnvironment = lib.optionalString hasSystemEnvironment ''
if [ -z "''${__NIXOS_SET_ENVIRONMENT_DONE-}" ] \
&& [ -z "''${__NIX_DARWIN_SET_ENVIRONMENT_DONE-}" ]; then
. ${lib.escapeShellArg systemEnvironmentFile} >/dev/null
fi
'';
captureEnvironment = ''
${lib.getExe' pkgs.coreutils "env"} -0
printf '\0'
${sourceSystemEnvironment}
. ${lib.escapeShellArg homeSessionVariablesFile} >/dev/null
${lib.getExe' pkgs.coreutils "env"} -0
'';
shouldLoadSystemEnvironment = lib.optionalString hasSystemEnvironment ''
or (
"__NIXOS_SET_ENVIRONMENT_DONE" not-in $env
and "__NIX_DARWIN_SET_ENVIRONMENT_DONE" not-in $env
)
'';
in
pkgs.writeText "hm-session-vars.nu" ''
if (
"__HM_SESS_VARS_SOURCED" not-in $env
${shouldLoadSystemEnvironment}
) {
let captured = (
^${pkgs.runtimeShell} -c ${toNushell { } captureEnvironment}
| complete
)

if $captured.exit_code != 0 {
error make {
msg: "failed to load the Home Manager session environment"
help: $captured.stderr
}
}

let separator = $"(char nul)(char nul)"
let sections = ($captured.stdout | split row $separator)

if ($sections | length) != 2 {
error make {
msg: "failed to parse the Home Manager session environment"
}
}

let before = (
$sections
| first
| split row (char nul)
| compact --empty
)
let changed = (
$sections
| last
| split row (char nul)
| compact --empty
| where { |entry| $entry not-in $before }
| parse --regex '(?s)^(?<name>[^=]+)=(?<value>.*)$'
| where { |entry| $entry.name not-in ["_" "_AST_FEATURES" "SHLVL"] }
| transpose --header-row --as-record
)
let changed = if "PATH" in $changed {
$changed | update PATH ($changed.PATH | split row (char esep))
} else {
$changed
}

load-env $changed
}
'';

linesOrSource =
name:
types.submodule (
Expand Down Expand Up @@ -279,8 +376,9 @@ in
}
)

(lib.mkIf (cfg.envFile != null || cfg.extraEnv != "") {
(lib.mkIf writeEnvFile {
"${cfg.configDir}/env.nu".text = lib.mkMerge [
(lib.mkIf hasSessionEnvironment "source ${sessionVariablesLoader}")
(lib.mkIf (cfg.envFile != null) cfg.envFile.text)
cfg.extraEnv
];
Expand Down
1 change: 1 addition & 0 deletions tests/modules/programs/nushell/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
nushell-example-settings = ./example-settings.nix;
nushell-config-dir = ./config-dir.nix;
nushell-session-variables = ./session-variables.nix;
}
2 changes: 1 addition & 1 deletion tests/modules/programs/nushell/env-expected.nu
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
source /nix/store/00000000000000000000000000000000-hm-session-vars.nu
$env.FOO = 'BAR'

6 changes: 2 additions & 4 deletions tests/modules/programs/nushell/example-settings.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
}
'';

envFile.text = ''
$env.FOO = 'BAR'
'';
envFile.text = "$env.FOO = 'BAR'";

loginFile.text = ''
# Prints "Hello, World" upon logging into tty1
Expand Down Expand Up @@ -75,7 +73,7 @@
"${configDir}/config.nu" \
${./config-expected.nu}
assertFileContent \
"${configDir}/env.nu" \
"$(normalizeStorePaths "${configDir}/env.nu")" \
${./env-expected.nu}
assertFileContent \
"${configDir}/login.nu" \
Expand Down
80 changes: 80 additions & 0 deletions tests/modules/programs/nushell/session-variables.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
config,
lib,
pkgs,
realPkgs,
...
}:

let
systemEnvironment = realPkgs.writeText "set-environment" ''
export __NIXOS_SET_ENVIRONMENT_DONE=1
export SYSTEM_VALUE="$HOME/$USER"
export PATH="/system/bin:$PATH"
'';
expected = pkgs.writeText "expected-session-variables" ''
/runtime/home/runtime-user
/runtime/home/.config
fallback
home-is-set
/runtime/home/.local/bin:/system/bin
env-file-loaded
'';
in
{
_module.args.osConfig.system.build.setEnvironment = systemEnvironment;

nixpkgs.overlays = [
(_final: _prev: { runtimeShell = "/bin/sh"; })
];

home = {
sessionPath = [ "$HOME/.local/bin" ];
sessionVariables = {
ALTERNATE_VALUE = "\${HOME:+home-is-set}";
DEFAULT_VALUE = "\${UNSET_VARIABLE:-fallback}";
EXPANDED_HOME = "$HOME/.config";
};
};

programs.nushell = {
enable = true;
package = realPkgs.nushell;
envFile.text = ''
$env.USER_ENV_FILE = "env-file-loaded"
'';
};

nmt.script =
let
homePrefix = "${config.home.homeDirectory}/";
configDir = lib.removePrefix homePrefix config.programs.nushell.configDir;
envFile = "home-files/${configDir}/env.nu";
in
''
assertFileRegex ${envFile} \
'^source /nix/store/[^/]*-hm-session-vars.nu$'

actual="$PWD/actual"
env -i \
HOME=/runtime/home \
USER=runtime-user \
PATH=/initial/bin \
${lib.getExe realPkgs.nushell} \
--no-history \
--config /dev/null \
--env-config "$(_abs ${envFile})" \
--commands '
print ([
$env.SYSTEM_VALUE
$env.EXPANDED_HOME
$env.DEFAULT_VALUE
$env.ALTERNATE_VALUE
($env.PATH | first 2 | str join (char esep))
$env.USER_ENV_FILE
] | str join (char newline))
' > "$actual"

assertFileContent "$actual" ${expected}
'';
}
Loading