this post was submitted on 25 May 2024
10 points (100.0% liked)

Nix / NixOS

1740 readers
3 users here now

Main links

Videos

founded 1 year ago
MODERATORS
 

My solution:

let

  nixFilesInDirectory = directory:
    (
      map (file: "${directory}/${file}")
      (
        builtins.filter
          (
            nodeName:
              (builtins.isList (builtins.match ".+\.nix$" nodeName)) &&
              # checking that it is NOT a directory by seeing
              # if the node name forcefully used as a directory is an invalid path
              (!builtins.pathExists "${directory}/${nodeName}/.")
          )
          (builtins.attrNames (builtins.readDir directory))
      )
    );

  nixFilesInDirectories = directoryList:
    (
      builtins.concatMap
        (directory: nixFilesInDirectory directory)
        (directoryList)
    );
  # ...
in {
  imports = nixFilesInDirectories ([
      "${./programs}"
      "${./programs/terminal-niceties}"
  ]);
  # ...
}

snippet from the full source code: quazar-omega/home-manager-config (L5-L26)

credits:


I'm trying out Nix Home Manager and learning its features little by little.
I've been trying to split my app configurations into their own files now and saw that many do the following:

  1. Make a directory containing all the app specific configurations:
programs/
└── helix.nix
  1. Make a catch-all file default.nix that selectively imports the files inside:
programs/
├── default.nix
└── helix.nix

Content:

{
  imports = [
    ./helix.nix
  ];
}
  1. Import the directory (picking up the default.nix) within the home-manager configuration:
{
  # some stuff...
  imports = [
    ./programs
  ];
 # some other stuff...
}

I'd like to avoid having to write each and every file I'll create into the imports of default.nix, that kinda defeats the point of separating it if I'll have to specify everything anyway, so is there a way to do so? I haven't found different ways to do this in various Nix discussions.


Example I'm looking at: https://github.com/fufexan/dotfiles/blob/main/home/terminal/default.nix

My own repository: https://codeberg.org/quazar-omega/home-manager-config

you are viewing a single comment's thread
view the rest of the comments
[–] QuazarOmega@lemy.lol 1 points 5 months ago* (last edited 5 months ago)

I've analyzed the script a bit (..ok for more than 2 hours + 2 of refactoring), because the first time I absolutely didn't understand it, now I've got it, but still, I won't ever understand, why make the syntax so confusing?
My system definition shouldn't be a codegolfing competition (•ˋ _ ˊ•)

TL;DR: I liked your script as a challenge to learn even more and I'm glad I did! Now I know a quite a bit more about the functions that Nix provides and how to use them, not a lot, but better than my previous almost 0, so thank you!

Anyways, here's the unmangled thing explained for anyone else who's interested (wrote for plain evaluation, it's interesting what each part outputs):

{
  /*
  builtins.unsafeGetAttrPos "_" { _ = null; }

  yields:
  {
    column = 46;
    file = "/path/to/this/slightly-unholy-file-finder.nix";
    line = 14;
  };

  you want to get the value of the name (which is the "key" in this key-value list) "file"
  */
  filePath = (builtins.unsafeGetAttrPos "_" { _ = null; }).file; # absolute path to current file
  directoryEntries = builtins.readDir ./.;

  entryNames = map
    (node: "./${node}")
    (
      # get name of files
      builtins.attrNames
        (
          /**
          use the function from before to remove this file right here
          from the set (NOT a list) of nodes found by readDir
          (may be files, dirs, etc.)
          
          Why?
          Because we start reading from the path ./
          which is where this file is located, of course
          */
          builtins.removeAttrs
            (builtins.readDir ./.)
            [
              /*
              get the current file name with some built-in, 
              either un- or poorly documented function black magic fuckery
              (I really wasn't able to find any proper documentation on this function)
              */
              (baseNameOf (builtins.unsafeGetAttrPos "_" { _ = null; }).file)
            ]
        )
    );
}

run it with:

nix eval -f ./slightly-unholy-file-finder.nix

There were multiple problems with this solution as I tried it:

  1. The missing baseName on line 39 which was needed to actually filter out the file path of the script that is being ran, because the paths I got out of readDir were relative (just for me? Did I change something in my environment? I'm not usre, the docs aren't very clear)
  2. It doesn't filter out files that are not .nix files
  3. It doesn't filter out directories (may be intentional though, I personally don't think that's a great idea as far as I got)

I'll post later my own further improved solution starting from my own (tbh, by now more like our) script.