diff --git a/lib/fileset/default.nix b/lib/fileset/default.nix index 9ccbf0ed7ce7..75e609a072e7 100644 --- a/lib/fileset/default.nix +++ b/lib/fileset/default.nix @@ -604,6 +604,7 @@ in { ({ name :: String, type :: String, + hasExt :: String -> Bool, ... } -> Bool) -> Path @@ -614,7 +615,7 @@ in { fileFilter (file: file.name == "default.nix") ./. # Include all non-Nix files from the current directory - fileFilter (file: ! hasSuffix ".nix" file.name) ./. + fileFilter (file: ! file.hasExt "nix") ./. # Include all files that start with a "." in the current directory fileFilter (file: hasPrefix "." file.name) ./. @@ -634,6 +635,12 @@ in { - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file. This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path. + - `hasExt` (String -> Bool): Whether the file has a certain file extension. + `hasExt ext` is true only if `hasSuffix ".${ext}" name`. + + This also means that e.g. for a file with name `.gitignore`, + `hasExt "gitignore"` is true. + Other attributes may be added in the future. */ predicate: diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix index 2fddf0d02285..35d556e78391 100644 --- a/lib/fileset/internal.nix +++ b/lib/fileset/internal.nix @@ -52,6 +52,7 @@ let concatStringsSep substring stringLength + hasSuffix ; in @@ -797,9 +798,11 @@ rec { if predicate { inherit name type; + hasExt = ext: hasSuffix ".${ext}" name; + # To ensure forwards compatibility with more arguments being added in the future, # adding an attribute which can't be deconstructed :) - "lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file }:`, use `{ name, file, ... }:` instead." = null; + "lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file, hasExt }:`, use `{ name, file, hasExt, ... }:` instead." = null; } then type diff --git a/lib/fileset/tests.sh b/lib/fileset/tests.sh index 81376bc451b1..077aefe371c3 100755 --- a/lib/fileset/tests.sh +++ b/lib/fileset/tests.sh @@ -847,7 +847,7 @@ checkFileset 'fileFilter (file: abort "this is not needed") ./.' # The predicate must be able to handle extra attributes touch a -expectFailure 'toSource { root = ./.; fileset = fileFilter ({ name, type }: true) ./.; }' 'called with unexpected argument '\''"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you'\''re using `\{ name, file \}:`, use `\{ name, file, ... \}:` instead."'\' +expectFailure 'toSource { root = ./.; fileset = fileFilter ({ name, type, hasExt }: true) ./.; }' 'called with unexpected argument '\''"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you'\''re using `\{ name, file, hasExt \}:`, use `\{ name, file, hasExt, ... \}:` instead."'\' rm -rf -- * # .name is the name, and it works correctly, even recursively @@ -895,6 +895,39 @@ expectEqual \ 'toSource { root = ./.; fileset = union ./d/a ./d/b; }' rm -rf -- * +# Check that .hasExt checks for the file extension +# The empty extension is the same as a file ending with a . +tree=( + [a]=0 + [a.]=1 + [a.b]=0 + [a.b.]=1 + [a.b.c]=0 +) +checkFileset 'fileFilter (file: file.hasExt "") ./.' + +# It can check for the last extension +tree=( + [a]=0 + [.a]=1 + [.a.]=0 + [.b.a]=1 + [.b.a.]=0 +) +checkFileset 'fileFilter (file: file.hasExt "a") ./.' + +# It can check for any extension +tree=( + [a.b.c.d]=1 +) +checkFileset 'fileFilter (file: + all file.hasExt [ + "b.c.d" + "c.d" + "d" + ] +) ./.' + # It's lazy tree=( [b]=1