Imports depending on nixpkgs release

Hi,

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 ?

I’m currently running NixOS 23.11 on x64.

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.

You have to first import Nixpkgs, and then evaluate the module system for whatever application you have. Here’s an example for standalone Home Manager: home-damager/lib.nix at 7fad2085682b28ab0dc9027584af0fe8fee57d09 · fricklerhandwerk/home-damager · GitHub and how to use it: home-damager/example.nix at 7fad2085682b28ab0dc9027584af0fe8fee57d09 · fricklerhandwerk/home-damager · GitHub

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 in configuration.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.

Thanks, that’s indeed quite involved.

It’s a bit more hacky, but I found that I could achieve my goal by loading <nixpkgs> in a temporary variable just to get the version.

So I ended up with something like

imports = let
  nixpkgs-version = (import <nixpkgs> {}).lib.trivial.release;
  home-manager = builtins.fetchTarball { url = https://github.com/nix-community/home-manager/archive/release-${nixpkgs-version}.tar.gz"; };
in 
  [ "${home-manager}/nixos" ];