How to make `src = ./.` in a flake.nix not change a lot?

This is happening because of 2 things:

  1. filterSource uses the name of the directory the filter is applied to as the store path name (the part after the hash) for the one it creates. It is not possible to specify the name yourself using filterSource.
  2. Nix moves all the files tracked by the version control inside the self repository (i.e. directory the flake belongs to) to the Nix store. This happens even before the evaluation of the flake starts. So the flake evaluates inside a directory named <hash>-source in the Nix store, where <hash> depends on all the files tracked by the versions control.

During the evaluation, filterSource will create a directory named <hash2>-<hash>-source that includes the files that passed the filter you specified. Since <hash2> also depends on <hash>-source, we get different hashes even if the change is in a file that didn’t pass the filter.

I think this means builtins.filterSource is useless inside a flake.

Using builtins.path, which allows specifying the store path name, like below should fix the problem.

src = builtins.path {
  path = ./.;
  name = "sile";
  filter = path: type:
    let
      ignoreFiles = [
        # Nix files
        "flake.nix"
        "flake.lock"
        "default.nix"
        "shell.nix"
        # git commit and editing format files
        ".commitlintrc.yml"
        "package.json"
        ".husky"
        ".editorconfig"
        # CI files
        ".cirrus.yml"
        "action.yml"
        "azure-pipelines.yml"
        "Dockerfile"
        # Git files
        ".github"
        ".gitattributes"
        ".gitignore"
        ".git"
      ];
    in
    # If the `path` checked is found in ignoreFiles, don't add it to the source
    if pkgs.lib.lists.any (p: p == (baseNameOf path)) ignoreFiles then
      false
    else
      true
    ;
};

Also I suggest nix-filter rather than creating the filtering boilerplate yourself in every project.

correction

I realized my previous answer was not entirely correct. It included this paragraph:

Source directories without an explicit name are not reproducible. The default name uses the basename of the current directory. Without flakes, this doesn’t change as long as you don’t rename the project directory. So it might be harder for you to discover the reproducibility issue without flakes. Also see Best practices — nix.dev documentation

The mentioned impurity is only valid for non-flake evaluations, because flake uses source as the store path name, regardless of the directory name. So saying it might be harder to discover without flakes was wrong.

4 Likes