lib.fileset.gitTracked/gitTrackedWith: init
A configuration parameter for gitTrackedWith will be introduced in the next commit
This commit is contained in:
parent
91c993afb9
commit
2dfb1d36cf
lib
@ -12,6 +12,7 @@ let
|
|||||||
_printFileset
|
_printFileset
|
||||||
_intersection
|
_intersection
|
||||||
_difference
|
_difference
|
||||||
|
_mirrorStorePath
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit (builtins)
|
inherit (builtins)
|
||||||
@ -596,4 +597,91 @@ in {
|
|||||||
# We could also return the original fileset argument here,
|
# We could also return the original fileset argument here,
|
||||||
# but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
|
# but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
|
||||||
actualFileset;
|
actualFileset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
|
||||||
|
|
||||||
|
This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
gitTracked :: Path -> FileSet
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# Include all files tracked by the Git repository in the current directory
|
||||||
|
gitTracked ./.
|
||||||
|
|
||||||
|
# Include only files tracked by the Git repository in the parent directory
|
||||||
|
# that are also in the current directory
|
||||||
|
intersection ./. (gitTracked ../.)
|
||||||
|
*/
|
||||||
|
gitTracked =
|
||||||
|
/*
|
||||||
|
The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
|
||||||
|
This directory must contain a `.git` file or subdirectory.
|
||||||
|
*/
|
||||||
|
path:
|
||||||
|
# See the gitTrackedWith implementation for more explanatory comments
|
||||||
|
let
|
||||||
|
fetchResult = builtins.fetchGit path;
|
||||||
|
in
|
||||||
|
if ! isPath path then
|
||||||
|
throw "lib.fileset.gitTracked: Expected the argument to be a path, but it's a ${typeOf path} instead."
|
||||||
|
else if ! pathExists (path + "/.git") then
|
||||||
|
throw "lib.fileset.gitTracked: Expected the argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
|
||||||
|
else
|
||||||
|
_mirrorStorePath path fetchResult.outPath;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
|
||||||
|
The first argument allows configuration with an attribute set,
|
||||||
|
while the second argument is the path to the Git working tree.
|
||||||
|
If you don't need the configuration,
|
||||||
|
you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead.
|
||||||
|
|
||||||
|
This is equivalent to the result of [`unions`](#function-library-lib.fileset.unions) on all files returned by [`git ls-files`](https://git-scm.com/docs/git-ls-files)
|
||||||
|
(which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default).
|
||||||
|
|
||||||
|
:::{.warning}
|
||||||
|
Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit)
|
||||||
|
As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store,
|
||||||
|
without being re-usable by [`toSource`](#function-library-lib.fileset.toSource).
|
||||||
|
|
||||||
|
This may change in the future.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Type:
|
||||||
|
gitTrackedWith :: { } -> Path -> FileSet
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# Include all files tracked by the Git repository in the current directory
|
||||||
|
gitTracked { } ./.
|
||||||
|
*/
|
||||||
|
gitTrackedWith =
|
||||||
|
{
|
||||||
|
}:
|
||||||
|
/*
|
||||||
|
The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
|
||||||
|
This directory must contain a `.git` file or subdirectory.
|
||||||
|
*/
|
||||||
|
path:
|
||||||
|
let
|
||||||
|
# This imports the files unnecessarily, which currently can't be avoided
|
||||||
|
# because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
|
||||||
|
# With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
|
||||||
|
# the unnecessarily import could be avoided.
|
||||||
|
# However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
|
||||||
|
fetchResult = builtins.fetchGit {
|
||||||
|
url = path;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
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.
|
||||||
|
# Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
|
||||||
|
# even though `git ls-files` wouldn't return any files in that case.
|
||||||
|
else if ! pathExists (path + "/.git") then
|
||||||
|
throw "lib.fileset.gitTrackedWith: Expected the second argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
|
||||||
|
else
|
||||||
|
_mirrorStorePath path fetchResult.outPath;
|
||||||
}
|
}
|
||||||
|
@ -825,4 +825,23 @@ rec {
|
|||||||
${baseNameOf root} =
|
${baseNameOf root} =
|
||||||
fromFile (baseNameOf root) rootType;
|
fromFile (baseNameOf root) rootType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# - The store path must not include files that don't exist in the respective local path.
|
||||||
|
#
|
||||||
|
# Type: Path -> String -> FileSet
|
||||||
|
_mirrorStorePath = localPath: storePath:
|
||||||
|
let
|
||||||
|
recurse = focusedStorePath:
|
||||||
|
mapAttrs (name: type:
|
||||||
|
if type == "directory" then
|
||||||
|
recurse (focusedStorePath + "/${name}")
|
||||||
|
else
|
||||||
|
type
|
||||||
|
) (builtins.readDir focusedStorePath);
|
||||||
|
in
|
||||||
|
_create localPath
|
||||||
|
(recurse storePath);
|
||||||
}
|
}
|
||||||
|
@ -95,8 +95,9 @@ expectEqual() {
|
|||||||
# Usage: expectStorePath NIX
|
# Usage: expectStorePath NIX
|
||||||
expectStorePath() {
|
expectStorePath() {
|
||||||
local expr=$1
|
local expr=$1
|
||||||
if ! result=$(nix-instantiate --eval --strict --json --read-write-mode --show-trace \
|
if ! result=$(nix-instantiate --eval --strict --json --read-write-mode --show-trace 2>"$tmp"/stderr \
|
||||||
--expr "$prefixExpression ($expr)"); then
|
--expr "$prefixExpression ($expr)"); then
|
||||||
|
cat "$tmp/stderr" >&2
|
||||||
die "$expr failed to evaluate, but it was expected to succeed"
|
die "$expr failed to evaluate, but it was expected to succeed"
|
||||||
fi
|
fi
|
||||||
# This is safe because we assume to get back a store path in a string
|
# This is safe because we assume to get back a store path in a string
|
||||||
@ -1251,6 +1252,150 @@ expectEqual 'trace (intersection ./a (fromSource (lib.cleanSourceWith {
|
|||||||
}))) null' 'trace ./a/b null'
|
}))) null' 'trace ./a/b null'
|
||||||
rm -rf -- *
|
rm -rf -- *
|
||||||
|
|
||||||
|
## lib.fileset.gitTracked/gitTrackedWith
|
||||||
|
|
||||||
|
# The first/second argument has to be a path
|
||||||
|
expectFailure 'gitTracked null' 'lib.fileset.gitTracked: Expected the argument to be a path, but it'\''s a null instead.'
|
||||||
|
expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it'\''s a null instead.'
|
||||||
|
|
||||||
|
# The path has to contain a .git directory
|
||||||
|
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.'
|
||||||
|
|
||||||
|
# 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() {
|
||||||
|
# All files listed by `git ls-files`
|
||||||
|
expectedFiles=()
|
||||||
|
while IFS= read -r -d $'\0' file; do
|
||||||
|
# If there are submodules but --recurse-submodules isn't passed,
|
||||||
|
# `git ls-files` lists them as empty directories,
|
||||||
|
# we need to filter that out since we only want to check/count files
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
expectedFiles+=("$file")
|
||||||
|
fi
|
||||||
|
done < <(git ls-files -z)
|
||||||
|
|
||||||
|
storePath=$(expectStorePath 'toSource { root = ./.; fileset = gitTrackedWith { } ./.; }')
|
||||||
|
|
||||||
|
# Check that each expected file is also in the store path with the same content
|
||||||
|
for expectedFile in "${expectedFiles[@]}"; do
|
||||||
|
if [[ ! -e "$storePath"/"$expectedFile" ]]; then
|
||||||
|
die "Expected file $expectedFile to exist in $storePath, but it doesn't.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||||
|
fi
|
||||||
|
if ! diff "$expectedFile" "$storePath"/"$expectedFile"; then
|
||||||
|
die "Expected file $expectedFile to have the same contents as in $storePath, but it doesn't.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is a cheap way to verify the inverse: That all files in the store path are also expected
|
||||||
|
# We just count the number of files in both and verify they're the same
|
||||||
|
actualFileCount=$(find "$storePath" -type f -printf . | wc -c)
|
||||||
|
if [[ "${#expectedFiles[@]}" != "$actualFileCount" ]]; then
|
||||||
|
die "Expected ${#expectedFiles[@]} files in $storePath, but got $actualFileCount.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Runs checkGitTrackedWith, this will make more sense in the next commit
|
||||||
|
checkGitTracked() {
|
||||||
|
checkGitTrackedWith
|
||||||
|
}
|
||||||
|
|
||||||
|
createGitRepo() {
|
||||||
|
git init -q "$1"
|
||||||
|
# Only repo-local config
|
||||||
|
git -C "$1" config user.name "Nixpkgs"
|
||||||
|
git -C "$1" config user.email "nixpkgs@nixos.org"
|
||||||
|
# Get at least a HEAD commit, needed for older Nix versions
|
||||||
|
git -C "$1" commit -q --allow-empty -m "Empty commit"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Go through all stages of Git files
|
||||||
|
# See https://www.git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
|
||||||
|
|
||||||
|
# Empty repository
|
||||||
|
createGitRepo .
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Untracked file
|
||||||
|
echo a > a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Staged file
|
||||||
|
git add a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Committed file
|
||||||
|
git commit -q -m "Added a"
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Edited file
|
||||||
|
echo b > a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Removed file
|
||||||
|
git rm -f -q a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
|
# gitignored file
|
||||||
|
createGitRepo .
|
||||||
|
echo a > .gitignore
|
||||||
|
touch a
|
||||||
|
git add -A
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Add it regardless (needs -f)
|
||||||
|
git add -f a
|
||||||
|
checkGitTracked
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
|
# Directory
|
||||||
|
createGitRepo .
|
||||||
|
mkdir -p d1/d2/d3
|
||||||
|
touch d1/d2/d3/a
|
||||||
|
git add d1
|
||||||
|
checkGitTracked
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
|
# Submodules
|
||||||
|
createGitRepo .
|
||||||
|
createGitRepo sub
|
||||||
|
|
||||||
|
# Untracked submodule
|
||||||
|
git -C sub commit -q --allow-empty -m "Empty commit"
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Tracked submodule
|
||||||
|
git submodule add ./sub sub >/dev/null
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Untracked file
|
||||||
|
echo a > sub/a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Staged file
|
||||||
|
git -C sub add a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Committed file
|
||||||
|
git -C sub commit -q -m "Add a"
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Changed file
|
||||||
|
echo b > sub/b
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
# Removed file
|
||||||
|
git -C sub rm -f -q a
|
||||||
|
checkGitTracked
|
||||||
|
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
# TODO: Once we have combinators and a property testing library, derive property tests from https://en.wikipedia.org/wiki/Algebra_of_sets
|
# TODO: Once we have combinators and a property testing library, derive property tests from https://en.wikipedia.org/wiki/Algebra_of_sets
|
||||||
|
|
||||||
echo >&2 tests ok
|
echo >&2 tests ok
|
||||||
|
@ -25,11 +25,13 @@ let
|
|||||||
];
|
];
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
nix
|
nix
|
||||||
|
pkgs.gitMinimal
|
||||||
] ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools;
|
] ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools;
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
} ''
|
} ''
|
||||||
datadir="${nix}/share"
|
datadir="${nix}/share"
|
||||||
export TEST_ROOT=$(pwd)/test-tmp
|
export TEST_ROOT=$(pwd)/test-tmp
|
||||||
|
export HOME=$(mktemp -d)
|
||||||
export NIX_BUILD_HOOK=
|
export NIX_BUILD_HOOK=
|
||||||
export NIX_CONF_DIR=$TEST_ROOT/etc
|
export NIX_CONF_DIR=$TEST_ROOT/etc
|
||||||
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
|
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
|
||||||
|
Loading…
Reference in New Issue
Block a user