containers / toolbox

Tool for interactive command line environments on Linux
https://containertoolbx.org/
Apache License 2.0
2.51k stars 214 forks source link

$ZDOTDIR/.zshenv is not being sourced in the login shell #1454

Closed notfirefox closed 3 weeks ago

notfirefox commented 7 months ago

Describe the bug $ZDOTDIR/.zshenv is not being sourced, instead the login shell only sources $HOME/.zshenv

Steps how to reproduce the behaviour

  1. mkdir -p ~/.config/zsh
  2. echo 'export TEST_VAR="hello"' > ~/.config/zsh/.zshenv
  3. export ZDOTDIR="~/.config/zsh"
  4. toolbox enter
  5. echo $TEST_VAR

Expected behaviour TEST_VAR is set.

Actual behaviour TEST_VAR is not set.

Screenshots none

Output of toolbox --version (v0.0.90+) toolbox version 0.0.99.5

Toolbx package info (rpm -q toolbox) toolbox-0.0.99.5-2.fc39.x86_64

Output of podman version

Client:       Podman Engine
Version:      4.9.0
API Version:  4.9.0
Go Version:   go1.21.6
Built:        Wed Jan 24 11:07:27 2024
OS/Arch:      linux/amd64

Podman package info (rpm -q podman) podman-4.9.0-1.fc39.x86_64

Info about your OS Fedora KDE 39

Additional context toolbox enter spawns an interactive shell that is supposed to act as if it was spawned on the host system. As such it is expected that it picks up the location of the shell configuration, i.e. the ZDOTDIR environment variable, and reads the files from there, e.g. $ZDOTDIR/.zshenv and $ZDOTDIR/.zshrc.

debarshiray commented 7 months ago

I don't use Z shell much myself, so this might be a stupid question, but how do you set the ZDOTDIR environment variable on the host?

Generally speaking, Toolbx expects most environment variables to be set by the shell's start-up scripts, just like they get set on the host, because the the home directory is shared. Of course, there are always some special cases that need to be handled separately.

I can't find any recommendation on setting ZDOTDIR in the zsh documentation. Most people seem to set it in ~/.zshenv and then have the bulk of their configuration under ZDOTDIR. This approach should work for Toolbx containers. Is it not working? Or do you have another approach?

I am just trying to understand the situation.

notfirefox commented 7 months ago

@debarshiray Okay yeah I might owe you that much. So I will begin with a short overview of the files that Z shell uses.

Commands are first read from /etc/zshenv; [...]

Commands are then read from $ZDOTDIR/.zshenv. If the shell is a login shell, commands are read from /etc/zprofile and then $ZDOTDIR/.zprofile. Then, if the shell is interactive, commands are read from /etc/zshrc and then $ZDOTDIR/.zshrc. Finally, if the shell is a login shell, /etc/zlo‐ gin and $ZDOTDIR/.zlogin are read.

For our intends and purposes, we can ignore /etc/*, so the order roughly look like this:

  1. $ZDOTDIR/.zshenvalways
  2. $ZDOTDIR/.zprofilelogin shell
  3. $ZDOTDIR/.zshrcinteractive shell
  4. $ZDOTDIR/.zloginlogin shell

Now there are likely numerous ways to set ZDOTDIR – but I will just focus on my use case for now.

[user@host ~/.config/zsh]$ tree # output has been edited to only show relevant details
├── plugins/
├── LICENSE
├── README.md
├── .zshenv
└── .zshrc

This is what my ZDOTDIR looks like. It mostly consists of two files.

Now the issue here is the following: ZDOTDIR needs to be set before .zshenv gets loaded, because I want $ZDOTDIR/.zshenv to be loaded. This means that ZDOTDIR needs to be set very early.

What I do is setting ZDOTDIR inside of $HOME/.zprofile and this might seem like a odd choice, because that file gets sourced rather late. The reason why that works is that every time I interact with a terminal and therefore with a shell, that shell is spawned after the login shell. This means that the ZDOTDIR environment variable gets set once by the login shell. After that every time I open a terminal window and as such a shell, ZDOTDIR is already set.

When I run toolbox enter, I expect it to behave exactly the same way as when I open a terminal on my host machine. toolbox enter spawns a login shell and the following happens:

# File $ZDOTDIR Description
1 zshenv ZDOTDIR is not set, so it will fall back to $HOME/.zshenv.
2 zprofile ZDOTDIR is not set, so it will fall back to $HOME/.zprofile.
3 zshrc ~/.zsh/config ZDOTDIR is set, so it will source $ZDOTDIR/.zshrcz.
4 zlogin ~/.zsh/config ZDOTDIR is set, so it will source $ZDOTDIR/.zlogin.

Every subsequent Z shell inside of toolbox will be able to source $ZDOTDIR/.zshenv, but the shell that toolbox enter drops you into will not. On the host machine, the login shell will already have sourced zprofile and therefore I can rely on that for every shell that I start after that.

So to summarize: The issue is that ZDOTDIR needs to be set before Z shell attempts to source zshenv. This works on the host machine, because every shell that the user (usually) interacts with is spawned after the login shell and as such ZDOTDIR has already been set by the login shell. The shell that toolbox enter drops you into fails to source $ZDOTDIR/.zshenv, because it discards the ZDOTDIR environment variable, that has been set by the login shell on the host machine.

For reference I will also link my Z shell config here. It should be fairly easy to get a grasp of it.