Asterix sybol on an import path in nix language?

Hello I’m using home manager and trying to import all my separate files in one go with the following script inside my home.nix:

imports = [
''etc/nixos/apps/*nix''
];

If I do them one by one it works but not with *

Import does no globbing. What you wrote expects a file with the name *nix.
What you want is implemented e.g. by Haumea: Haumea - Filesystem-based module system for Nix

2 Likes

Hello, I know this may have come as “Feed me the answer with a silver spoon” but I’m quite new at nix space. From what I understand (which is probably wrong) I need a thing called flakes to use Haumea and I haven’t got to using flakes yet. Can you explain the steps I should take a little further? Even if I don’t need to use flakes I still didn’t get the gist of Haumea thingy.

1 Like

Well, Haumea itself does not rely on Flakes, but Flakes are the most comfortable way to pull Haumea in as a dependency.

Assuming you have imported Haumea (e.g. from its default.nix) as haumea, essentially you would start with something like

apps = haumea.lib.load {
      src = /etc/nixos/apps/;
      inputs = {
        inherit (nixpkgs); # or whatever you pass to your individual nix files
      };
    };

# now apps is an attribute set mapping filenames to what you would have gotten from individual imports
# to flatten it to a list: (map (key: getAttr key apps) (attrNames apps));

@figsoda @blaggacao You can likely provide more insight on using Haumea without flakes and using the imports.

the only difference is that without flakes, all the stuff are directly under default.nix instead of lib, so it would be haumea.load instead of haumea.lib.load

in the haumea repository, import ./. { } is roughly equivalent to (builtins.getFlake (toString ./.)).lib

No actually I couldn’t even get to importing part since the documentation literally starts with flakes. In fact everything on the documentation is about flakes… I really don’t know what to do.

I really wish there was a way to do regular expressions without Haumea.

you can basically load the nix files with builtins.readDir. builtins.attrNames will result in a list of files of a directory.

So my may get something like that.

nix-repl> builtins.attrNames (builtins.readDir ./profiles/nixos) 
[ "base.nix" "desktop.nix" "gaming.nix" "nix.nix" "optimized.nix" "server.nix" "telegraf.nix" "user-config.nix" ]

Which is some logic i am using to load modules in a similar way you are asking for.
So if you dont want do trag in Haumea that may be also a way of doing that.

You can find my thing here. (be aware that i am using flake-parts and that logic is inside some parts glue code)
My usecase is that i want to export modules to the flakes top level, without naming them in some default.nix, as i keep forgetting to update those, to i tried to eliminate that thing.
I highly recommend to not copy random code from the internet (as thats kind of untrustful code :slight_smile: ).
Also i am not generally recommending my code here, but it works super fine for me.

Do:

haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) {} ;
# haumea.load ...

Well, technically you can do this without Haumea, but you would have to implement the functionality on your own,…
(you would then probably end up with something similar to this: https://github.com/nix-community/haumea/blob/79fc9824f06bbd80e3f994716c600ba299aafd17/src/load.nix)

I need you to be a little more specific than that, do that to where?

I tried to put the line haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) {} ; anywhere in my configuration.nix file where I see fit yet none of them worked.

Also you said, # haumea.load ... so am I supposed to do that on just a rooted terminal?

You need to put that in a let block or make your configuration a recursive attribute set, then you can put that statement in your configuration.

No. You would use the load function in your configuration as I suggested above.

That said, keep the individual listing of files and focus on enjoying working with Nix/NixOS. Once you are more familiar with Nix as a programming language come back to this topic.

Okey so, I had to remove last slash from here because it gave an error:

Also lib didn’t existed so I just removed it and changed it into haumea.load

Yet I’m still having the following error:

       error: The option `home-manager.users.user.apps' does not exist. Definition values:
       - In `/etc/nixos/configuration.nix':
           {
             micro = {
               programs = {
                 micro = {
                   enable = true;
           ...

I should add none of the lines are in configuration.nix so I’m royally confused.

Could you show us what your configuration.nix and home.nix look like now? I feel like we can’t debug this properly if we don’t see the full setup.

For sure!
configuration.nix:


  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      <home-manager/nixos>
    ];

  # Bootloader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking.hostName = "nixos"; # Define your hostname.
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Enable networking
  networking.networkmanager.enable = true;

  # Set your time zone.
  time.timeZone = "mytimezone";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";

  i18n.extraLocaleSettings = {
    LC_ADDRESS = "en_GB.UTF-8";
    LC_IDENTIFICATION = "en_GB.UTF-8";
    LC_MEASUREMENT = "en_GB.UTF-8";
    LC_MONETARY = "en_GB.UTF-8";
    LC_NAME = "en_GB.UTF-8";
    LC_NUMERIC = "en_GB.UTF-8";
    LC_PAPER = "en_GB.UTF-8";
    LC_TELEPHONE = "en_GB.UTF-8";
    LC_TIME = "en_GB.UTF-8";
  };

  # Enable the X11 windowing system.
  services.xserver.enable = true;

  # Enable the KDE Plasma Desktop Environment.
  services.xserver.displayManager.sddm.enable = true;
  services.xserver.displayManager.sddm.theme = "sddm-theme-dialog";
  services.xserver.desktopManager.plasma5.enable = true;
  services.xserver.windowManager.openbox.enable = true;

  # Configure keymap in X11
  services.xserver = {
    layout = "--";
    xkbVariant = "";
  };

  # Configure console keymap
  console.keyMap = "--";

  # Enable CUPS to print documents.
  services.printing.enable = true;

  # Enable sound with pipewire.
  sound.enable = true;
  hardware.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    # If you want to use JACK applications, uncomment this
    jack.enable = true;

    # use the example session manager (no others are packaged yet so this is enabled by default,
    # no need to redefine it in your config for now)
    #media-session.enable = true;
  };

  # Enable touchpad support (enabled default in most desktopManager).
  # services.xserver.libinput.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.user = {
    isNormalUser = true;
    description = "user";
    extraGroups = [ "networkmanager" "wheel" ];
    packages = with pkgs; [
      firefox
      kate
      musescore
      openbox
      ly
	  nix-prefetch
	  nix-prefetch-github
	  git
    #  thunderbird
    ];
  };

  home-manager = {
    useGlobalPkgs = true;
    useUserPackages = true;
    users.user = import ./home.nix;
  };

  # Allow unfree packages
  nixpkgs.config.allowUnfree = true;

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  environment.systemPackages = with pkgs; [
    (callPackage /home/user/Downloads/sddm-theme-dialog.nix {}).sddm-theme-dialog
  #  vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
  #  wget
  ];

  # Some programs need SUID wrappers, can be configured further or are
  # started in user sessions.
  # programs.mtr.enable = true;
  # programs.gnupg.agent = {
  #   enable = true;
  #   enableSSHSupport = true;
  # };

  # List services that you want to enable:

  # Enable the OpenSSH daemon.
  # services.openssh.enable = true;

  # Open ports in the firewall.
  # networking.firewall.allowedTCPPorts = [ ... ];
  # networking.firewall.allowedUDPPorts = [ ... ];
  # Or disable the firewall altogether.
  # networking.firewall.enable = false;

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "23.05"; # Did you read the comment?

}

home.nix:

{ config, pkgs, ... }:

let
  haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) {} ;
in
{
apps = haumea.load {
      src = /etc/nixos/apps;
      inputs = {
        inherit (nixpkgs);
      };
    };

  home.username = "user";
  home.homeDirectory = "/home/user";
  home.stateVersion = "23.05";
  home.packages = with pkgs; [
    htop
    micro
  ];
}

I changed some data such as, username and region so they are not actually broken It’s just for privacy reasons.

Ahhh right I see. I think the confusion comes from what haumea.load actually returns and what you wanted to do with it.

This is entirely accurate, that option doesn’t exist in home-manger, but nix is evaluated lazily, so the error message triggers later than you might expect. Specifically:

  home-manager = {
    useGlobalPkgs = true;
    useUserPackages = true;
    users.user = import ./home.nix;
  };

The last line here just tells Nix that home-manager.users.user should evaluate to whatever is in home.nix. But this happens before Nix tries to find out what home.nix even contains. home.nix is not fully evaluated on import. This is a little surprising, but luckily we know that home-manager.users.user.apps is not defined in configuration.nix, but home.nix, so we can look for the issue there.

So, what does haumea.load actually return? I would really recommend you try this stuff out yourself so you can get a better understanding of how the language works, but just for illustration, I created a small example:

$ tree haumea-test
haumea-test
├── a-set.nix
├── first.nix
├── second.nix
└── somepkgs.nix
$ for f in haumea-test/*; do echo "$f:"; cat $f; echo; done
haumea-test/a-set.nix:
{ }: { help = "Please talk to your manager."; truth = "1+1=2"; }

haumea-test/first.nix:
1

haumea-test/second.nix:
{ }: 2

haumea-test/somepkgs.nix:
{ pkgs }:
with pkgs; [
  vim
  hello
  jq
]

And now, I’ll import this with haumea in nix repl so we can inspect it a little bit:

$ nix repl '<nixpkgs>'
warning: future versions of Nix will require using `--file` to load a file
Welcome to Nix 2.15.1. Type :? for help.

Loading installable ''...
Added 16207 variables.
nix-repl> haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) { inherit lib; }

nix-repl> x = haumea.load { src = ./haumea-test; inputs = { inherit pkgs lib; }; }

Note that at this point, haumea wasn’t even downloaded yet! This is a great example of lazy evaluation.

The variable haumea exists and we have defined what it should contain, but we never looked at it. We also defined x which will evaluate to the result of calling haumea.load, but we never looked at that result either, so Nix had no need to actually check what haumea.load even is. This is the reason that a huge Nix expression like nixpkgs can even exist, and the reason why Nix has its own language as opposed to re-using an existing language.

Anyway, let’s continue and actually evaluate x now (output formatted manually):

nix-repl> :p x
{ 
  a-set = {
    help = "Please talk to your manager.";
    truth = "1+1=2";
  };
  first = 1;
  second = 2;
  somepkgs = [
    «derivation /nix/store/n3xngbhk3adgfmn2y75gi7n581jyiach-vim-9.0.1562.drv»
    «derivation /nix/store/8rgfc52k0529kypam0jy5p1a4jsj4dbq-hello-2.12.1.drv»
    «derivation /nix/store/chx4kpsg2wahdmzv80gzfn4hnxfafvqr-jq-1.6.drv»
  ];
}

So as you can see, haumea basically takes all .nix files in the directory you specify, and put the results of evaluating them into a set, where the key for each result is the name of the file it came from. If the file evaluates to a function, it will call that function with the arguments you supplied via inputs. In your case, that definition was also erroneous:

This evaluates to an empty set, see “Inheriting Attributes”.

I don’t know what the structure of your files in /etc/nixos/apps is, but I assume they are also functions that take some inputs, potentially pkgs and lib?

You wrote in your initial post that you’re just trying to import the files:

So I assume instead of setting the option apps, you set imports. Just make it clear, while it doesn’t look like it, adding a string to imports basically works the same as if you imported the file directly. So we now have two options:

  1. Turn the attribute set generated by haumea.load into a list so imports understands it
  2. Change the behaviour of haumea.load so it only outputs the paths for each file

While the second can be achieved, the first is much simpler now that we’ve got haumea.load to work; we can just use builtins.attrValues. And so the final code in your home.nix would look like this:

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

let
  haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) { inherit lib; } ;
  apps = haumea.load {
    src = /etc/nixos/apps;
    inputs = {
      inherit pkgs; # Not sure if this is correct, see below
    };
  };
in
{
  imports = builtins.attrValues apps;

  home.username = "user";
  home.homeDirectory = "/home/user";
  home.stateVersion = "23.05";
  home.packages = with pkgs; [
    htop
    micro
  ];
}

As I said, I don’t know what the structure of the files in /etc/nixos/apps is, so potentially you need to change how inputs is specified.

Finally, if you’re wondering how the hell you should’ve figured this out yourself, I want to point you at two awesome resources: MyNixOS where you can search for home-manager and NixOS options easily, and noogle, which will help you find useful functions like the builtins.attrValues we used above.

Hope this helps! Stay strong in your learning journey, it’s absolutely worth it!

3 Likes

Thank you! Just had to type:

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

instead of:

{ config, pkgs, ... }:

and change:

  haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) { inherit lib; } ;

to:

  haumea = import (builtins.fetchGit https://github.com/nix-community/haumea?rev=v0.2.2) { inherit pkgs.lib; } ;

I hope this will be helpful for everyone!
Also thanks for noogle and mynixos, they certainly will come in handy.

I guess you mean the other way around? But yeah, good point, what I wrote wasn’t syntactically correct. I edited my post.

Alternatively, you could use

  haumea = import (builtins.fetchGit ...) { inherit (pkgs) lib; } ;

Anyway, glad to hear that it’s working now!

1 Like