Setting an environment variable with home-manager after programs are initialized

I want to set the following environment variable:

ZSH_AUTOSUGGEST_STRATEGY=(atuin_auto atuin_global)

The problem is that programs.atuin.enable = true; puts atuin init zsh in the bottom part of .zshrc, so if I put my variable in sessionVariables or initExtra it gets overwritten by atuin.

My current workaround is adding the following to initExtra:

        function my_precmd() {
          export ZSH_AUTOSUGGEST_STRATEGY=(atuin_auto atuin_global)
        }
        autoload -U add-zsh-hook
        add-zsh-hook precmd my_precmd

Is there a simpler way to set this variable in a way that doesn’t get overwritten?

initExtraFirst would not overwrite the value. completionInit may also be more appropriate.

If neither of those quite does it, just look through the options and see if you can find one that puts the commands where you want them, there’s plenty of hooks.

Thanks for the response! I’ve looked through the options, but as far as I can tell there are no hooks that put it further down than initExtra. initExtraFirst and completionInit both are added before initExtra. loginExtra would be later, but I am not using a login shell.

You could try lib.mkAfter to add some extra config to the end of the file (this is untested as I’m on my phone so it might need some adjustment or not even work at all)

home.file.".zshrc".text = lib.mkAfter ''
  export VAR=VALUE
'';
3 Likes

Ah, I see, I misunderstoodnthe issue. I wonder if this is worth changing upstream, programs.atuin simply uses initExtra as well, maybe it should be using mkBefore.

Thanks for the tip! Since atuin was also adding stuff to initExtra I was able to solve it with a variant of your solution:

programs.zsh.initExtra = lib.mkAfter ''
  export ZSH_AUTOSUGGEST_STRATEGY=(atuin_auto atuin_global)
'';

Which has approximately the same result but results in slightly more cohesive code in my case.

Thanks! That would have prevented my specific issue, but it seems like putting stuff in initExtra is quite common:

If this is the usual way of integrating a program with zsh then I can understand keeping it simple and standardized.

I don’t think programs.atuin should use lib.mkBefore, because that would make it more difficult for end users to do something before its initialization. Now you have both options with lib.mkBefore and lib.mkAfter.

There are multiple options built into home-manager to do things before initExtra without needing to use lib.mkBefore or lib.mkAfter. E.g. initExtraFirst, initExtraBeforeCompInit, envExtra.

For me personally, the latter would be easier. I’m quite new to Nix, but in my opinion figuring out which options home-manager has is relatively easy. The docs of the options state quite clearly where in .zshrc/.zshenv they will be put,

For the first option you need to both understand that programs like atuin add their initialisation to initExtra, for which you’d need to read the home-manager source code, and that you can manipulate where strings are added by using lib.mkBefore and lib.mkAfter. I guess this option would be easier for people with general Nix knowledge and not much home-manager knowledge.

To be clear I don’t think having those extra options is bad, but rather that I think that home manager should refrain from using lib.mk{Before,After} if possible so that those are still available to the end user.

Though if I’m not mistaken home manager also comes with the dag type so that makes me wonder why that wasn’t used instead of the many initExtra options. It would solve the problem in a much better way.

1 Like