Is there a way to install unstable home-manager packages in a stable version?

I have a system with flakes and home-manager, and I have home-manager declared like this:

flake.nix:

home-manager = {
    url = "github:nix-community/home-manager/release-23.11";
    inputs.nixpkgs.follows = "nixpkgs";
};

bemenu is currently only available in home-manager unstable. So, I can not do the following: programs.bemenu.enable = true; (Note: the solution described here will not work, as it will throw a …programs.bemenu does not exist error). But, maybe there is a way to do something like unstable-programs.bemenu.enable = true;?

For nixpkgs one can create an overlay to access unstable packages.

Possibly, I can declare the following input in flake.nix:

home-manager-unstable = {
     url = "github:nix-community/home-manager";
     inputs.nixpkgs.follows = "nixpkgs-unstable";
 };

But, with my limited Nix understanding, I have not managed to create a similar overlay for home-manager programs, as for nixpkgs. Is it possible to reach unstable home-manager programs in a stable version (like unstable-programs.bemenu.enable = true;)? If yes, does anyone know how to achieve this?

1 Like

I might to be wrong, but I don’t think it’s possible. In fact, I’d be happy to be wrong here as I don’t like the solution I have used for this.

The reason I think this way is that programs.foo is a concept of the module system, not packages – so overlays are not applicable for the configuration side. Of course you can pass derivation from an overlay to programs.foo.package, but the option programs.foo.package needs to exist in the modules.

It’s just not possible to set up “these modules’ options will now be called programs-foo and services-foo and whatnot” in Nix module system. Maybe it’s possible with some gnarly lib.evalModules trickery but that feels more brittle than the solution below.

One approach for your use-case is to try to import the module by hand. You could try importing the programs.bemenu module by specifying the path inside home-manager-unstable input. However, this would not bring the closure of the module in. I.e. if programs.bemenu would rely on some base home-manager machinery that is not present in stable – an error would be thrown.

This works:

{
  inputs = {
    nixpkgs.url = "nixpkgs/nixos-23.11";
    home-manager = {
      url = "github:nix-community/home-manager/release-23.11";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    home-manager-unstable.url = "github:nix-community/home-manager";
  };

  outputs =
    inputs@{ self, nixpkgs, ... }:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in
    {
      checks.${system}.test = pkgs.testers.runNixOSTest {
        name = "test";
        node.specialArgs = {
          inherit (self) inputs outputs;
        };
        nodes.machine1 =
          _: # { config, pkgs, ... }:
          {
            services.getty.autologinUser = "alice";
            imports = [ inputs.home-manager.nixosModules.home-manager ];
            users.users.alice = {
              isNormalUser = true;
              password = "hunter2";
            };

            home-manager.extraSpecialArgs = {
              inherit (self) inputs outputs;
            };
            home-manager.users.alice =
              _: # { config, ... }: # config is home-manager's config, not the OS one
              {
                imports = [ (inputs.home-manager-unstable + "/modules/programs/bemenu.nix") ];
                home.stateVersion = "23.11";
                programs.bemenu.enable = true;
              };
          };
        # If developing a proper test script, see
        # https://nixos.org/manual/nixos/stable/#ssec-machine-objects
        testScript = "start_all()";
      };
    };
}
1 Like

It is possible with the NixOS module system: NixOS Manual

I don’t remember off the top of my head if the home-manager module system permits the same thing. May need to check the source code if the attribute is named differently or something if it doesn’t just work :tm:

1 Like

that’s not the same thing though, is it? Replacing modules is a combination of disabling a module through .disabledModules and then importing its replacement. This is not a mechanism to mass-rename options coming from an import – which is what I was referring to as not being possible, at least trivially.

In this case, bemenu module does not exist in stable home-manager, so there is nothing to disable and the code in my post provides an example of importing a module by path from a flakeref.

For simple “leaf” modules like bemenu it works. However, say, foo.nix module depends on options’ values from bar.nix and both foo.nix and bar.nix are only present in unstable – then the consumer would need to manually import the “closure” of necessary options. Or, maybe, try lib.evalModules and extracting its results.

1 Like

Thanks a lot for the input, guys. I really appreciate it!

I thought there would be a nice and simple way to achieve this. Since there is not, I might just stick to installing bemenu from nixpgks instead.

I think the poetic waxing made my first post confusing. Sorry about that :slight_smile:

Specifically for your use-case – importing bemenu module from unstable – it seems perfectly possible. You’d need to have an extra input pointing to just home-manager (you have that already) and you would need to import the module by path like so:

home-manager.users.alice =
  _: # { config, ... }: # config is home-manager's config, not the OS one
  {
    imports = [ (inputs.home-manager-unstable + "/modules/programs/bemenu.nix") ];
    home.stateVersion = "23.11";
    programs.bemenu.enable = true;
  };

The code sample in that post was of a flake that showcases this import and one could run as nix run -L .#checks.x86_64-linux.test.driverInteractive.

As usual – real code would depend on where you’re doing the import and how home-manager-unstable is passed to the import site.

2 Likes

Ah, yep, but if you’re ok with programs.bemenu simply referring to the unstable version instead of the stable version that’s all there is to it. You shouldn’t need the original anyway.

No worries, @VTimofeenko. It is due to my lack of understanding of Nix that it was confusing to me. I appreciate the clarification, and feeding me the simple solution to my problem.


Side note:
When I implemented the solution, I ended up with:

… while evaluating the module argument `inputs` in ":anon-5:anon-1":
error: infinite recursion encountered

I realised I had forgotten this line:
home-manager.extraSpecialArgs = {inherit inputs;};
Rebuilding with that in place, it worked like a charm. No idea why, but I am sure there is a logical explanation to that as well.

It’s hard to say for certain but it’s most likely related to the way home-manager is imported. Check this thread for a similar error in similar context

1 Like