zsh: load session vars from zprofile for login shells

Zsh reads .zshenv before system login startup files. On NixOS and macOS those later files can replace or reorder PATH, so home.sessionPath entries loaded from .zshenv may disappear or move behind system paths.

Keep .zshenv coverage for non-login shells, but source Home Manager session variables from .zprofile for login shells so PATH-like values are applied after system login setup.

Fixes #2991
This commit is contained in:
Austin Horstman
2026-05-18 15:06:13 -05:00
parent d0af9b8bf3
commit 04ec113f8b
5 changed files with 71 additions and 22 deletions

View File

@@ -0,0 +1,18 @@
{ config, ... }:
{
time = "2026-05-18T17:00:00+00:00";
condition = config.programs.zsh.enable;
message = ''
The `programs.zsh` module now sources Home Manager session variables from
`.zprofile` for login shells. Non-login shells continue to source them from
`.zshenv`.
As a result, Home Manager now always writes `.zprofile` when
`programs.zsh.enable` is set, instead of only writing it when
`programs.zsh.profileExtra` is configured.
This keeps `home.sessionPath` entries from being overwritten or reordered
by system login startup files such as NixOS' `/etc/zprofile` or macOS'
`path_helper`.
'';
}

View File

@@ -373,6 +373,21 @@ in
let
envVarsStr = config.lib.zsh.exportAll cfg.sessionVariables { indent = " "; };
localVarsStr = config.lib.zsh.defineAll cfg.localVariables;
sessionVarsStr = lib.removeSuffix "\n" ''
# Environment variables
. "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh"
# Only source this once
if [[ -z "''${__HM_ZSH_SESS_VARS_SOURCED-}" ]]; then
export __HM_ZSH_SESS_VARS_SOURCED=1
${envVarsStr}
fi
'';
indentNonEmptyLines =
str:
concatStringsSep "\n" (
map (line: if line == "" then "" else " ${line}") (lib.splitString "\n" str)
);
aliasesStr = concatStringsSep "\n" (
lib.mapAttrsToList (
@@ -441,10 +456,6 @@ in
home.file."${dotDirRel}/.zshenv".text = cfg.envExtra;
})
(mkIf (cfg.profileExtra != "") {
home.file."${dotDirRel}/.zprofile".text = cfg.profileExtra;
})
(mkIf (cfg.loginExtra != "") {
home.file."${dotDirRel}/.zlogin".text = cfg.loginExtra;
})
@@ -478,15 +489,18 @@ in
{
home.file."${dotDirRel}/.zshenv".text = ''
# Environment variables
. "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh"
# Only source this once
if [[ -z "''${__HM_ZSH_SESS_VARS_SOURCED-}" ]]; then
export __HM_ZSH_SESS_VARS_SOURCED=1
${envVarsStr}
if [[ ! -o login ]]; then
${indentNonEmptyLines sessionVarsStr}
fi
'';
home.file."${dotDirRel}/.zprofile".text = ''
${sessionVarsStr}
''
+ optionalString (cfg.profileExtra != "") ''
${cfg.profileExtra}
'';
}
{

View File

@@ -18,5 +18,7 @@
nmt.script = ''
assertFileExists home-files/.zshenv
assertFileContent $(normalizeStorePaths home-files/.zshenv) ${./session-variables.zshenv}
assertFileExists home-files/.zprofile
assertFileContent $(normalizeStorePaths home-files/.zprofile) ${./session-variables.zprofile}
'';
}

View File

@@ -0,0 +1,13 @@
# Environment variables
. "/nix/store/00000000000000000000000000000000-hm-session-vars.sh/etc/profile.d/hm-session-vars.sh"
# Only source this once
if [[ -z "${__HM_ZSH_SESS_VARS_SOURCED-}" ]]; then
export __HM_ZSH_SESS_VARS_SOURCED=1
export IS_EMPTY=""
export IS_FALSE=false
export IS_TRUE=true
export PATH="$HOME/bin:$PATH"
export V1="v1"
export V2="v2-v1"
fi

View File

@@ -1,13 +1,15 @@
# Environment variables
. "/nix/store/00000000000000000000000000000000-hm-session-vars.sh/etc/profile.d/hm-session-vars.sh"
if [[ ! -o login ]]; then
# Environment variables
. "/nix/store/00000000000000000000000000000000-hm-session-vars.sh/etc/profile.d/hm-session-vars.sh"
# Only source this once
if [[ -z "${__HM_ZSH_SESS_VARS_SOURCED-}" ]]; then
export __HM_ZSH_SESS_VARS_SOURCED=1
export IS_EMPTY=""
export IS_FALSE=false
export IS_TRUE=true
export PATH="$HOME/bin:$PATH"
export V1="v1"
export V2="v2-v1"
# Only source this once
if [[ -z "${__HM_ZSH_SESS_VARS_SOURCED-}" ]]; then
export __HM_ZSH_SESS_VARS_SOURCED=1
export IS_EMPTY=""
export IS_FALSE=false
export IS_TRUE=true
export PATH="$HOME/bin:$PATH"
export V1="v1"
export V2="v2-v1"
fi
fi