Import paths relative to the root of the flake without --impure

Is it possible to define imports relative to the root of the flake directory (while still keeping pure evaluation mode)?
I would like to avoid having to refactor file paths every time I move a module around.

Suppose I have a flake that defines my nixos system and a bunch of modules:

flake-root/
- flake.nix
- configurations/foo/default.nix
- configurations/foo/nginx.nix
- modules/services/postgres.nix
- modules/services/forgejo.nix

Inside the flake I have:

eachDefaultSystemYaddaYadda({
  nixosConfigurations.foo = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    specialArgs = { inherit inputs; };
    modules = [ ./configurations/foo ];
  };
})

Is it possible to have something akin to the following inside a module:

{ inputs, ... }: {
  imports = [
    (inputs.self + configurations/foo/nginx.nix)
    (inputs.self + modules/services/postgres.nix)
    (inputs.self + modules/services/forgejo.nix)
  ];
}

This specific version does not work since inputs.self contains the path of the file that is being evaluated, so the path is duplicated ‘/nix/store/<hash>-source/configurations/foo/configurations/foo/nginx.nix' does not exist.

{ inputs, ... }:
{
  imports = [
    (inputs.self + "/configurations/foo/nginx.nix")
    (inputs.self + "/modules/services/postgres.nix")
    (inputs.self + "/modules/services/forgejo.nix")
  ];
}
1 Like
{ inputs, ... }: {
  imports = [
    (inputs.self + /configurations/foo/nginx.nix)
    (inputs.self + /modules/services/postgres.nix)
    (inputs.self + /modules/services/forgejo.nix)
  ];
}

Your instance sounds like it should not be happening. Are you using any type of submodules or flakes as inputs with self references in them?

I hate to wax frameworks, but I went dendritic to avoid the exact thing you’re talking about. Pathing was by far the most annoying thing and not an ancient relic of the past in my configs. However, that severely breaks the “no refactor” request. :sweat_smile:

Because you’re using an incorrect relative path (which is resolved to the absolute store path) then appending that to another store path.

The leading / is important!

Assuming I understand what you’re trying to do, and supposing this is in configurations/foo.nix, you could define a local variable with the relative path to the flake root:

{ ... }:
let flake_root = ../;
in {
  imports = [
    ./${flake_root}/configurations/foo/nginx.nix
    ./${flake_root}/modules/services/postgres.nix
    ./${flake_root}/modules/services/forgejo.nix
  ];
}

You would have to change the let flake_root = line when you move the module, but not anything else. The leading ./ on each of the imports is unfortunately necessary due to the syntax of path literals.

1 Like

This does not work because it needs impure evaluation.

Thanks a lot for all the replies.
@FrdrCkII‘s solution works.

No, I don’t think so.

If I may ask.. What are wax frameworks?

I missed the leading / in the issue since it’s not something I use, but the others caught on to it and with good explanation.

“To wax” is a colloqualism, I suppose it’s an older way of saying “glazing”; it means carry on about or excessively talk or advertise.

1 Like

Thanks.
I want to use absolute paths mostly because relative paths increase the mental load for me and its only for my private flakes anyways.