Extend set attribute defined in another module

Hello,

I am facing an issue with my nix/nixos configuration flake.
The flake is managing multiple hosts with different systems, notably an aarch64 macos and a nixos.
I want my configuration to be as generic as possible and as factored as possible.
My zsh configuration is quite similar on all my systems but I need to add one line to the .zshrc and change one alias specifically on the macos configuration.

Currently, I achieve that by completly replacing the program.zsh.initExtra defined by my common configuration in the macos specific module, but I can’t find a way to append the needed commands to the already defined initExtra.

Here is a snippet to illustrate what I mean:

home/common/zsh/zsh.nix:

{ config, lib, pkgs, ... }:

{

  home.packages = with pkgs; [
    zsh
    powerline
    nerdfonts
    htop
    tree
  ];

  programs.zsh = {
      enable = true;
      enableAutosuggestions = false;
      enableCompletion = true;
      dotDir = ".config/zsh";
      oh-my-zsh = {
          enable = true;
	        theme = "agnoster";
	        plugins = [ "sudo" ];
      };
      sessionVariables = {
        EDITOR = "emacsclient -t -c";
        NIXDOTS = "~/.nixdots";
      };
      initExtra = ''
      alias ne="$EDITOR"
      alias sne="sudo $EDITOR"
      alias cdot="cd $NIXDOTS"
      alias devel="nix develop"
      alias nixrb="$NIXDOTS/system-rebuild.sh"
      alias nixup="$NIXDOTS/system-update.sh"
      '';
  };


  fonts.fontconfig.enable = true;

}

This module is imported in my macos specific module:

{ config, lib, pkgs, ... }:

{
  imports = [
    ../common/doom/doom.nix
    ../common/zsh/zsh.nix
  ];

  home.username = "meichel";
  home.homeDirectory = "/Users/meichel";

  home.stateVersion = "22.11";

  programs.home-manager.enable = true;

  nixpkgs = {
    config = {
      allowUnfree = true;
      allowUnfreePredicate = (_: true);
    };
  };


  home.packages = with pkgs; [
    git
    gource
    time
  ];

  programs.git = {
    enable = true;
    userName = "Jean-Malo Meichel";
    userEmail = "jean-malo.meichel@epita.fr";
    delta.enable = true;

    ignores = [
      "*~"
    ];
  };

  programs.zsh.initExtra = ''
      alias ne="$EDITOR"
      alias sne="sudo $EDITOR"
      alias cdot="cd $NIXDOTS"
      alias devel="nix develop"
      alias nixrb="$NIXDOTS/mac-rebuild.sh"
      source ~/.profile
  '';

}

This actually works as expected, but I think it is not well factorized. Is it possible to extend the initExtra attribute without replacing it ?
I tried the following syntax, but it creates an infinite recursion:

programs.zsh.initExtra = config.programs.zsh.initExtra + ''
    alias nixrb="$NIXDOTS/mac-rebuild.sh"
    source ~/.profile
  '';

I guess the cleanest solution is certainly to create a module for that, like in /common/zsh/zsh.nix:

{config, lib, pkgs}:
{
  options.myZshConfig = {
    enable = mkEnableOption "my zsh configuration";
    extraInitExtra = mkOption {
      type = types.str;
      default = "";
    }; 
  };
  config = mkIf config.myZshConfig.enable {
    programs.zsh = {
      enable = true;
      initExtra = ''
        alias ne="$EDITOR";
        ${config.myZshConfig.extraInitExtra}
      '';
    };
  };
}

And then you can call it in your configuration like:

{ config, lib, pkgs, ... }:

{
  imports = [
    ../common/doom/doom.nix
    ../common/zsh/zsh.nix
  ];
  myZshConfig = {
    enable = true;
    extraInitExtra = ''
      alias nixrb="$NIXDOTS/mac-rebuild.sh"
    '';
  };
}

Other options might involve defining functions etc, but it is not as clean and will quickly be a mess to maintain.

That is not actually what is happening. The programs.zsh.initExtra option has the lines type so by default, the module system merges all definitions.

I do not think the order is actually defined and you just got lucky that the lines from the macos config got appended after the ones from zsh.nix. If you want to be sure, you can use lib.mkAfter to make the priority explicit.

3 Likes