I am trying to import the home manager module and have it automatically follow the version of my nixpkgs. I imagined I could use the function pkgs.lib.trivial.release to get the current nixpkgs version, however trying to call it inside imports results in infinite recursion.
imports = let
home-manager = builtins.fetchTarball { url = https://github.com/nix-community/home-manager/archive/release-${pkgs.lib.trivial.release}.tar.gz"; };
in
[ "${home-manager}/nixos" ];
Playing around with builtins.trace, it seems that the infinite recursion only occurs when pkgs.lib is called in imports which makes sense according to this comment. How would you work around this ?
Yes, that can’t work as you’ve found. That’s because you (and most of the world before flakes, but this has nothing to do with flakes, it’s merely pure evaluation forcing the issue) are holding it backwards. This is also independent of NixOS.
Thanks @fricklerhandwerk for your help and explanation. To be honest, I have no idea in which order nix evaluates the configuration, let alone how to influence it…
I don’t really understand the example : could you explain how this would apply in a configuration.nix ?
Oh yeah, that’s the issue. You don’t apply it inconfiguration.nix but outside of it. Unfortunately there is no straightforward tooling for that, because for some reason no one in 15 years of NixOS seems to have bothered hard enough to make this seemingly obvious thing easy. The idea would be something along the same lines as here: Getting Started with Home Manager | NixOS & Flakes Book
But with flakes you can’t tie the Home Manager source to the Nixpkgs source, because you can’t do computation in the inputs attribute. In stable Nix, you can do this (code is tested):
let
pkgs = import <nixpkgs> { }; # for illustration only
version = with pkgs; lib.versions.majorMinor lib.version;
# XXX: this must be an impure reference.
# if `pkgs.fetchFromGitHub` was used here, specifying a commit hash would be required.
home-manager-src = fetchTarball {
name = "home-manager-${version}";
url = "https://github.com/nix-community/home-manager/tarball/release-${version}";
};
home-manager = import "${home-manager-src}/nixos";
# produce an environment with tools to manage a NixOS configuration
environment = configuration:
let
machine = (pkgs.nixos [ configuration ]);
switch = pkgs.writeShellApplication {
name = "switch";
# this is essentially what `nixos-rebuild` does
text = "${machine.config.system.build.toplevel}/activate";
};
in
pkgs.mkShellNoCC { packages = [ switch ]; } // machine;
in
rec {
myConfiguration =
let
stateVersion = "23.11"; # this must stay constant
in
{
imports = [ home-manager ];
users.users.eve.isNormalUser = true;
users.users.eve.initialPassword = "test"; # for illustration only
system.stateVersion = stateVersion;
home-manager.users.eve = { pkgs, ... }: {
home.packages = with pkgs; [ cowsay lolcat ];
home.stateVersion = stateVersion;
};
};
# enter this environment with `nix-shell -A myMachine`
# switch into this configuration:
# switch
# or build a VM:
# $(nix-build --no-out-link -A myMachine.config.system.build.vm -I nixpkgs=channel:nixos-23.11)/bin/run-nixos-vm
# to verify this works (e.g. in the VM), run:
# cowsay this is bananas | lolcat
myMachine = environment myConfiguration;
}
If you want to make it nice, you’d factor the generic bits out into a library to avoid bloating your entry point. And of course you can put your configuration into any file and just import it.