diff --git a/lib/fileset/default.nix b/lib/fileset/default.nix index 0248c16c645e..1ccd3013ce5c 100644 --- a/lib/fileset/default.nix +++ b/lib/fileset/default.nix @@ -13,14 +13,17 @@ let _intersection _difference _mirrorStorePath + _fetchGitSubmodulesMinver ; inherit (builtins) + isBool isList isPath pathExists seq typeOf + nixVersion ; inherit (lib.lists) @@ -35,6 +38,7 @@ let inherit (lib.strings) isStringLike + versionOlder ; inherit (lib.filesystem) @@ -650,14 +654,21 @@ in { ::: Type: - gitTrackedWith :: { } -> Path -> FileSet + gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet Example: # Include all files tracked by the Git repository in the current directory - gitTracked { } ./. + # and any submodules under it + gitTracked { recurseSubmodules = true; } ./. */ gitTrackedWith = { + /* + (optional, default: `false`) Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files. + + If `true`, this is equivalent to passing the [--recurse-submodules](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---recurse-submodules) flag to `git ls-files`. + */ + recurseSubmodules ? false, }: /* The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. @@ -672,9 +683,18 @@ in { # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944). fetchResult = builtins.fetchGit { url = path; + + # This is the only `fetchGit` parameter that makes sense in this context. + # We can't just pass `submodules = recurseSubmodules` here because + # this would fail for Nix versions that don't support `submodules`. + ${if recurseSubmodules then "submodules" else null} = true; }; in - if ! isPath path then + if ! isBool recurseSubmodules then + throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead." + else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then + throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used." + else if ! isPath path then throw "lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it's a ${typeOf path} instead." # We can identify local working directories by checking for .git, # see https://git-scm.com/docs/gitrepository-layout#_description. diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix index 93f3d5e18b96..0769e654c8fb 100644 --- a/lib/fileset/internal.nix +++ b/lib/fileset/internal.nix @@ -826,6 +826,10 @@ rec { fromFile (baseNameOf root) rootType; }; + # Support for `builtins.fetchGit` with `submodules = true` was introduced in 2.4 + # https://github.com/NixOS/nix/commit/55cefd41d63368d4286568e2956afd535cb44018 + _fetchGitSubmodulesMinver = "2.4"; + # Mirrors the contents of a Nix store path relative to a local path as a file set. # Some notes: # - The store path is read at evaluation time. diff --git a/lib/fileset/tests.sh b/lib/fileset/tests.sh index c7991e06bdf9..ef8bee9e66cf 100755 --- a/lib/fileset/tests.sh +++ b/lib/fileset/tests.sh @@ -1262,11 +1262,30 @@ expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the expectFailure 'gitTracked ./.' 'lib.fileset.gitTracked: Expected the argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.' expectFailure 'gitTrackedWith {} ./.' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.' +# recurseSubmodules has to be a boolean +expectFailure 'gitTrackedWith { recurseSubmodules = null; } ./.' 'lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it'\''s a null instead.' + +# recurseSubmodules = true is not supported on all Nix versions +if [[ "$(nix-instantiate --eval --expr "$prefixExpression (versionAtLeast builtins.nixVersion _fetchGitSubmodulesMinver)")" == true ]]; then + fetchGitSupportsSubmodules=1 +else + fetchGitSupportsSubmodules= + expectFailure 'gitTrackedWith { recurseSubmodules = true; } ./.' 'lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version 2.4 and after, but Nix version [0-9.]+ is used.' +fi + # Checks that `gitTrackedWith` contains the same files as `git ls-files` # for the current working directory. # If --recurse-submodules is passed, the flag is passed through to `git ls-files` # and as `recurseSubmodules` to `gitTrackedWith` checkGitTrackedWith() { + if [[ "${1:-}" == "--recurse-submodules" ]]; then + gitLsFlags="--recurse-submodules" + gitTrackedArg="{ recurseSubmodules = true; }" + else + gitLsFlags="" + gitTrackedArg="{ }" + fi + # All files listed by `git ls-files` expectedFiles=() while IFS= read -r -d $'\0' file; do @@ -1276,9 +1295,9 @@ checkGitTrackedWith() { if [[ -f "$file" ]]; then expectedFiles+=("$file") fi - done < <(git ls-files -z) + done < <(git ls-files -z $gitLsFlags) - storePath=$(expectStorePath 'toSource { root = ./.; fileset = gitTrackedWith { } ./.; }') + storePath=$(expectStorePath 'toSource { root = ./.; fileset = gitTrackedWith '"$gitTrackedArg"' ./.; }') # Check that each expected file is also in the store path with the same content for expectedFile in "${expectedFiles[@]}"; do @@ -1299,9 +1318,13 @@ checkGitTrackedWith() { } -# Runs checkGitTrackedWith, this will make more sense in the next commit +# Runs checkGitTrackedWith with and without --recurse-submodules +# Allows testing both variants together checkGitTracked() { checkGitTrackedWith + if [[ -n "$fetchGitSupportsSubmodules" ]]; then + checkGitTrackedWith --recurse-submodules + fi } createGitRepo() {