mirror of
https://github.com/nix-community/home-manager.git
synced 2026-11-07 14:59:33 +00:00
nushell: load session environment at runtime
Source the OS and Home Manager POSIX environment scripts when Nushell starts so runtime HOME and USER expansions and profile paths match managed POSIX shells.
This commit is contained in:
committed by
Austin Horstman
parent
2f5ab4a8fe
commit
6750249c69
@@ -332,6 +332,15 @@ The state version in this release includes the changes below. These
|
||||
changes are only active if the `home.stateVersion` option is set to
|
||||
"26.11" or later.
|
||||
|
||||
- [](#opt-programs.nushell.loadSessionVariables) defaults to `true` from
|
||||
`home.stateVersion = "26.11"`. Nushell loads the Home Manager session
|
||||
environment at startup and, when used as a NixOS or nix-darwin module,
|
||||
the system environment too. This makes Home Manager manage `env.nu`;
|
||||
migrate independently managed contents to [](#opt-programs.nushell.envFile)
|
||||
or [](#opt-programs.nushell.extraEnv), or disable the option.
|
||||
Earlier state versions retain the old behavior and emit an evaluation
|
||||
warning until `programs.nushell.loadSessionVariables` is explicitly set.
|
||||
|
||||
- On Darwin, the default value of
|
||||
[](#opt-programs.firefox.configPath) changes from
|
||||
`Library/Application Support/Firefox` to
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
osConfig,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
@@ -9,6 +10,72 @@ let
|
||||
inherit (lib.hm.nushell) isNushellInline toNushell;
|
||||
cfg = config.programs.nushell;
|
||||
|
||||
systemEnvironmentFile = osConfig.system.build.setEnvironment or null;
|
||||
hasSystemEnvironment = systemEnvironmentFile != null;
|
||||
homeSessionVariablesFile = "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh";
|
||||
|
||||
# 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
|
||||
'';
|
||||
# A script avoids Bash's SSH-specific bashrc loading for `bash -c`.
|
||||
captureEnvironment = pkgs.writeShellScript "hm-session-vars-capture" ''
|
||||
${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 (
|
||||
($env.__NIXOS_SET_ENVIRONMENT_DONE? | is-empty)
|
||||
and ($env.__NIX_DARWIN_SET_ENVIRONMENT_DONE? | is-empty)
|
||||
)
|
||||
'';
|
||||
in
|
||||
pkgs.writeText "hm-session-vars.nu" ''
|
||||
if (
|
||||
($env.__HM_SESS_VARS_SOURCED? | is-empty)
|
||||
${shouldLoadSystemEnvironment}
|
||||
) {
|
||||
let sections = (
|
||||
^${captureEnvironment}
|
||||
| split row $"(char nul)(char nul)"
|
||||
| each {
|
||||
split row (char nul)
|
||||
| parse --regex '(?s)^(?<name>[^=]+)=(?<value>.*)$'
|
||||
| transpose --header-row --as-record
|
||||
}
|
||||
)
|
||||
let before = $sections.0
|
||||
let after = $sections.1
|
||||
let ignored = ["_" "_AST_FEATURES" "SHLVL"]
|
||||
let changed = ($after | items { |name, value|
|
||||
if $name not-in $ignored and (
|
||||
$name not-in $before or ($before | get $name) != $value
|
||||
) {
|
||||
[$name $value]
|
||||
}
|
||||
} | compact | into record)
|
||||
|
||||
load-env $changed
|
||||
if "PATH" in $changed {
|
||||
$env.PATH = $changed.PATH | split row (char esep)
|
||||
}
|
||||
for name in ($before | columns) {
|
||||
if $name not-in $after and $name not-in $ignored {
|
||||
hide-env --ignore-errors $name
|
||||
}
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
linesOrSource =
|
||||
name:
|
||||
types.submodule (
|
||||
@@ -191,6 +258,36 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
loadSessionVariables = lib.mkOption {
|
||||
type = types.bool;
|
||||
inherit
|
||||
(lib.hm.deprecations.mkStateVersionOptionDefault {
|
||||
inherit (config.home) stateVersion;
|
||||
since = "26.11";
|
||||
optionPath = [
|
||||
"programs"
|
||||
"nushell"
|
||||
"loadSessionVariables"
|
||||
];
|
||||
legacy.value = false;
|
||||
current.value = true;
|
||||
})
|
||||
default
|
||||
defaultText
|
||||
;
|
||||
description = ''
|
||||
Whether to load the Home Manager session environment, such as
|
||||
{option}`home.sessionVariables` and {option}`home.sessionPath`, in
|
||||
{file}`env.nu` when Nushell starts without it, for example as a login
|
||||
shell. When Home Manager is used as a NixOS or nix-darwin module, the
|
||||
system environment is loaded too.
|
||||
|
||||
Enabling this makes Home Manager manage {file}`env.nu`. Put other
|
||||
environment configuration in {option}`programs.nushell.envFile` or
|
||||
{option}`programs.nushell.extraEnv`.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentVariables = lib.mkOption {
|
||||
type = types.attrsOf lib.hm.types.nushellValue;
|
||||
default = { };
|
||||
@@ -279,8 +376,9 @@ in
|
||||
}
|
||||
)
|
||||
|
||||
(lib.mkIf (cfg.envFile != null || cfg.extraEnv != "") {
|
||||
(lib.mkIf (cfg.loadSessionVariables || cfg.envFile != null || cfg.extraEnv != "") {
|
||||
"${cfg.configDir}/env.nu".text = lib.mkMerge [
|
||||
(lib.mkIf cfg.loadSessionVariables "source ${sessionVariablesLoader}")
|
||||
(lib.mkIf (cfg.envFile != null) cfg.envFile.text)
|
||||
cfg.extraEnv
|
||||
];
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
{
|
||||
nushell-example-settings = ./example-settings.nix;
|
||||
nushell-config-dir = ./config-dir.nix;
|
||||
nushell-session-variables = ./session-variables.nix;
|
||||
nushell-session-variables-legacy = import ./session-variables-state-version.nix {
|
||||
stateVersion = "26.05";
|
||||
};
|
||||
nushell-session-variables-current = import ./session-variables-state-version.nix {
|
||||
stateVersion = "26.11";
|
||||
};
|
||||
nushell-session-variables-opt-in = import ./session-variables-state-version.nix {
|
||||
stateVersion = "26.05";
|
||||
loadSessionVariables = true;
|
||||
};
|
||||
nushell-session-variables-opt-out = import ./session-variables-state-version.nix {
|
||||
stateVersion = "26.11";
|
||||
loadSessionVariables = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
stateVersion,
|
||||
loadSessionVariables ? null,
|
||||
}:
|
||||
{ lib, ... }:
|
||||
{
|
||||
home.stateVersion = stateVersion;
|
||||
programs.nushell = {
|
||||
enable = true;
|
||||
}
|
||||
// lib.optionalAttrs (loadSessionVariables != null) { inherit loadSessionVariables; };
|
||||
|
||||
nmt.script =
|
||||
let
|
||||
enabled =
|
||||
if loadSessionVariables != null then
|
||||
loadSessionVariables
|
||||
else
|
||||
lib.versionAtLeast stateVersion "26.11";
|
||||
envFile = "home-files/.config/nushell/env.nu";
|
||||
in
|
||||
if enabled then
|
||||
''
|
||||
assertFileRegex ${envFile} '^source /nix/store/[^/]*-hm-session-vars.nu$'
|
||||
''
|
||||
else
|
||||
''
|
||||
assertPathNotExists ${envFile}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
realPkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
systemEnvironment = pkgs.writeText "set-environment" ''
|
||||
if [ -n "''${SKIP_SYSTEM-}" ]; then return; fi
|
||||
export __NIXOS_SET_ENVIRONMENT_DONE=1
|
||||
export SYSTEM_VALUE="$HOME/$USER"
|
||||
export PATH="/system/bin:$PATH"
|
||||
unset REMOVED_BY_SYSTEM
|
||||
'';
|
||||
in
|
||||
{
|
||||
_module.args.osConfig.system.build.setEnvironment = systemEnvironment;
|
||||
|
||||
home = {
|
||||
stateVersion = "26.11";
|
||||
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$'
|
||||
|
||||
for ssh_client in "" "10.0.0.1 5000 22"; do
|
||||
env -i \
|
||||
HOME=/runtime/home \
|
||||
USER=runtime-user \
|
||||
PATH=/initial/bin \
|
||||
REMOVED_BY_SYSTEM=inherited \
|
||||
SSH_CLIENT="$ssh_client" \
|
||||
${lib.getExe realPkgs.nushell} \
|
||||
--login \
|
||||
--no-history \
|
||||
--config /dev/null \
|
||||
--env-config "$(_abs ${envFile})" \
|
||||
--commands '
|
||||
use std/assert
|
||||
assert equal $env.SYSTEM_VALUE /runtime/home/runtime-user
|
||||
assert equal $env.EXPANDED_HOME /runtime/home/.config
|
||||
assert equal $env.DEFAULT_VALUE fallback
|
||||
assert equal $env.ALTERNATE_VALUE home-is-set
|
||||
assert equal $env.PATH [/runtime/home/.local/bin /system/bin /initial/bin]
|
||||
assert equal $env.USER_ENV_FILE env-file-loaded
|
||||
assert ("REMOVED_BY_SYSTEM" not-in $env)
|
||||
' || fail "Nushell did not load the session environment."
|
||||
|
||||
# tmux on nix-darwin clears the marker to request reinitialization.
|
||||
for marker in __NIX_DARWIN_SET_ENVIRONMENT_DONE \
|
||||
__NIXOS_SET_ENVIRONMENT_DONE; do
|
||||
env -i \
|
||||
HOME=/runtime/home \
|
||||
USER=runtime-user \
|
||||
PATH=/initial/bin \
|
||||
__HM_SESS_VARS_SOURCED=1 \
|
||||
"$marker=" \
|
||||
REMOVED_BY_SYSTEM=inherited \
|
||||
SSH_CLIENT="$ssh_client" \
|
||||
${lib.getExe realPkgs.nushell} \
|
||||
--login \
|
||||
--no-history \
|
||||
--config /dev/null \
|
||||
--env-config "$(_abs ${envFile})" \
|
||||
--commands '
|
||||
use std/assert
|
||||
assert equal $env.SYSTEM_VALUE /runtime/home/runtime-user
|
||||
assert equal $env.PATH [/system/bin /initial/bin]
|
||||
assert ("EXPANDED_HOME" not-in $env)
|
||||
assert ("REMOVED_BY_SYSTEM" not-in $env)
|
||||
' || fail "Nushell did not reload with an empty $marker."
|
||||
done
|
||||
|
||||
# Reloading scripts that leave the environment unchanged is valid too.
|
||||
env -i \
|
||||
HOME=/runtime/home USER=runtime-user PATH=/initial/bin \
|
||||
__HM_SESS_VARS_SOURCED=1 SKIP_SYSTEM=1 \
|
||||
SSH_CLIENT="$ssh_client" \
|
||||
${lib.getExe realPkgs.nushell} \
|
||||
--login --no-history --config /dev/null \
|
||||
--env-config "$(_abs ${envFile})" \
|
||||
--commands '
|
||||
use std/assert
|
||||
assert equal $env.PATH [/initial/bin]
|
||||
assert equal $env.USER_ENV_FILE env-file-loaded
|
||||
' || fail "Nushell failed with an unchanged session environment."
|
||||
done
|
||||
# Model a remote Bash startup file that initializes the system first.
|
||||
mkdir -p ssh-home
|
||||
printf '%s\n' '. ${systemEnvironment}' > ssh-home/.bashrc
|
||||
for hm_marker in "" 1; do
|
||||
env -i \
|
||||
HOME="$PWD/ssh-home" USER=runtime-user PATH=/initial/bin \
|
||||
SSH_CLIENT="10.0.0.1 5000 22" \
|
||||
__HM_SESS_VARS_SOURCED="$hm_marker" \
|
||||
__NIX_DARWIN_SET_ENVIRONMENT_DONE= \
|
||||
${lib.getExe realPkgs.nushell} \
|
||||
--login --no-history --config /dev/null \
|
||||
--env-config "$(_abs ${envFile})" \
|
||||
--commands '
|
||||
use std/assert
|
||||
assert equal $env.SYSTEM_VALUE ($env.HOME + "/runtime-user")
|
||||
assert equal $env.__NIXOS_SET_ENVIRONMENT_DONE "1"
|
||||
' || fail "Nushell lost the system environment over SSH."
|
||||
done
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user