NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.31k stars 13.55k forks source link

Documentation: Set up XDG Base Directory environment variables #224525

Open SuperSamus opened 1 year ago

SuperSamus commented 1 year ago

Problem

XDG Base Directories are environment variables that allow to organize the location of configuration, data, etc. in the home directory. Even with the default values, they make the user's $HOME much cleaner: for example, Git by default stores its configuration in ~/.gitconfig, but with XDG_CONFIG_HOME set then it will be stored in XDG_CONFIG_HOME/git/config.

The problem is: in NixOS, how does one set up these environment variables declaratively?

A solution could be Home Manager, which has the option programs.xdg.enable (which needs to be combined with programs.<shell>.enable in order to work). However, using Home Manager for such a simple thing is a bit overkill.

Another solution, according to the Arch Wiki, would appear to be:

environment.sessionVariables = {
  XDG_CACHE_HOME  = "@{HOME}/.cache";
  XDG_CONFIG_HOME = "@{HOME}/.config";
  XDG_DATA_HOME   = "@{HOME}/.local/share";
  XDG_STATE_HOME  = "@{HOME}/.local/state";
}

However, environment.sessionVariables doesn't support @{HOME}. You could use \${HOME} instead, however it may not be initialized when pam_env sets up these environment variables. It happens for example when logging in via TTY to a Fish shell.

Basically, every way I found to set up these variables has a flaw.

Proposal

Maybe creating some NixOS options specifically for setting up the XDG Base Directories?

Checklist

rnhmjoj commented 1 year ago

You could use \${HOME}instead, however it may not be initialized when pam_env sets up these environment variables. It happens for example when logging in via TTY to a Fish shell.

I have been using this

  environment.sessionVariables = {
    XDG_CONFIG_HOME = "$HOME/etc";
    XDG_DATA_HOME   = "$HOME/var/lib";
    XDG_CACHE_HOME  = "$HOME/var/cache";
  };

for a number of years now and never noticed any issue.

SuperSamus commented 1 year ago

You could use \${HOME} instead, however it may not be initialized when pam_env sets up these environment variables. It happens for example when logging in via TTY to a Fish shell.

rnhmjoj commented 1 year ago

It works for me: I actually use fish and always log in a tty before starting the graphical session. I'm quite surprised to hear you say this.

SuperSamus commented 1 year ago

I get this error after logging in:

error: can not save history
warning-path: Unable to locate data directory derived from $XDG_DATA_HOME: '/.local/share/fish'.
warning-path: The error was 'Permission denied'.
warning-path: Please set $XDG_DATA_HOME to a directory where you have write access.
error: can not save universal variables or functions
warning-path: Unable to locate config directory derived from $XDG_CONFIG_HOME: '/.config/fish'.
warning-path: The error was 'Permission denied'.
warning-path: Please set $XDG_CONFIG_HOME to a directory where you have write access.
/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin/mkdir: cannot create directory þ/generated_completionsþ: Permission denied
mkdir: cannot create directory þ/completionsþ: Permission denied
mkdir: cannot create directory þ/conf.dþ: Permission denied
mkdir: cannot create directory þ/functionsþ: Permission denied
warning: An error occurred while redirecting file '/config.fish'
open: Permission denied
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
SuperSamus commented 1 year ago

I'll describe the setup and symptoms in more detail.

The computer I'm using has two users. System-wide, the shell is the default one (Bash).

My user has set users.myname.shell = pkgs.fish;. When logging in via TTY, $HOME isn't loaded before the other XDG envs. By launching fish inside it, though, this is fixed. Even without that, I can launch Plasma X11/Wayland just fine both via TTY and SDDM.

The other user however uses the default shell. When logging in via TTY, they have no problem. However, launching Plasma Wayland (and not X11) through SDDM, causes it to not load. Looking at journalctl, it's because the XDG envs aren't set up correctly.

@rnhmjoj Do you replicate these behaviors with the settings I just described?

rnhmjoj commented 1 year ago

No, I can't reproduce your issue. I just tried this:

nix-build -E '(import <nixpkgs/nixos> { configuration = { pkgs, ... }:
    { users.users.vm = {
        password = "vm";
        isNormalUser = true;
        shell = pkgs.fish;
     };
     environment.sessionVariables.XDG_CONFIG_HOME = "$HOME/etc";
    };
  }).vm'

Running the VM and logging in:

image

I also tried without shell = pkgs.fish, so bash, and the variable is also being set correctly. I'm not sure what could be causing this: I guess some other option/module is interfering or there is some kind of race condition, though the variable is always set reliably for me.

I would try to pinpoint the issue by removing parts of your configuration, or starting with nothing and adding piece by piece.

rnhmjoj commented 1 year ago

Anyway, I would be in favour of a more user friendly option to set these variables. There are already options like xdg.portal, xdg.mime, etc. so something like xdg.directories would be ideal.

SuperSamus commented 1 year ago

So, replacing \${HOME} with $HOME solved all the issues I mentioned above... why? I don't even know if I should complain... (Anyway, leaving open for the documentation/options factor)

jtojnar commented 1 year ago

Anyway, I would be in favour of a more user friendly option to set these variables. There are already options like xdg.portal, xdg.mime, etc. so something like xdg.directories would be ideal.

Those other options are required to have a XDG compliant system. But you should never need to set those variables unless you want to override the defaults so having the option under xdg namespace feels dirty. At best, you could name it xdg.setBasedirEnvironmentVariablesToDefaultValuesForNoncompliantApps.

rnhmjoj commented 1 year ago

But you should never need to set those variables unless you want to override the defaults

Yes, that's exactly the reason to have this option. I'm not sure what non-compliant applications have to do with this: they are not going to read the variables anyway.

jtojnar commented 1 year ago

They are non-compliant in that they do not use the implicit default values as described in the basedir spec. Maybe we could call them “partially compliant” but I felt that was long enough and it sounds a bit like “partially pregnant”.

rnhmjoj commented 1 year ago

They are non-compliant in that they do not use the implicit default values

I've never come across anything like this. Anyway, I think the point of these variables is to customise the default locations of the XDG directories, without setting hunders of application-specific variables. For example, I like them to be visible so I rename them to etc, bin, var. I don't understand why you're saying this is something "dirty" to have in the xdg namespace.

l0b0 commented 6 months ago

For the record, these are the defaults mentioned in the XDG Base Directory Specification:

{
  environment.sessionVariables = {
    XDG_CACHE_HOME = "$HOME/.cache";
    XDG_CONFIG_DIRS = "/etc/xdg";
    XDG_CONFIG_HOME = "$HOME/.config";
    XDG_DATA_DIRS = "/usr/local/share/:/usr/share/";
    XDG_DATA_HOME = "$HOME/.local/share";
    XDG_STATE_HOME = "$HOME/.local/state";
  };
}

(Please note XDG_CONFIG_DIRS, XDG_DATA_DIRS, and others are specified by other options; in my case (output pretty-printed with alejandra):

❯ nix repl --file '<nixpkgs/nixos>'
nix-repl> pkgs.lib.attrsets.filterAttrs (name: value: pkgs.lib.strings.hasPrefix "XDG_" name) config.environment.sessionVariables    
{
  XDG_CACHE_HOME = "$HOME/.cache";
  XDG_CONFIG_DIRS = "/etc/xdg";
  XDG_CONFIG_HOME = "$HOME/.config";
  XDG_DATA_DIRS = "/nix/store/vihazwfjqbwl76gzpsj3r9nr5bbsmw4q-gnome-mimeapps/share:/nix/store/nh03g2vz8by412ywv1kbqqlnrzk9h7cm-desktops/share";
  XDG_DATA_HOME = "$HOME/.local/share";
  XDG_DESKTOP_PORTAL_DIR = "/nix/store/bif15j4523chg85ydf99d7mjjmmsk4ss-xdg-portals/share/xdg-desktop-portal/portals";
  XDG_STATE_HOME = "$HOME/.local/state";
}

You might want to omit or lib.mkForce these.)

Please let me know if there are any omissions or errors.

nixos-discourse commented 3 months ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/why-does-nixos-not-set-xdg-config-home-by-default/45296/6