diff --git a/lix/libexpr/builtins/fetchGit.md b/lix/libexpr/builtins/fetchGit.md index ea8cdc23f..b14bc7619 100644 --- a/lix/libexpr/builtins/fetchGit.md +++ b/lix/libexpr/builtins/fetchGit.md @@ -48,6 +48,10 @@ attribute with the following attributes (all except `url` optional): With this argument being true, it's possible to load a `rev` from *any* `ref` (by default only `rev`s from the specified `ref` are supported). +- `narHash` + + If given, the source is first looked-up in the Nix store and the [substituters](@docroot@/command-ref/conf-file.md#conf-substituters), and only fetched if not available. + The return value is an attrset containing the following keys: - `lastModified` (`integer`) diff --git a/lix/libexpr/primops/fetchTree.cc b/lix/libexpr/primops/fetchTree.cc index b793640f2..69414b33e 100644 --- a/lix/libexpr/primops/fetchTree.cc +++ b/lix/libexpr/primops/fetchTree.cc @@ -169,6 +169,13 @@ static void fetchTree( "attribute 'name' isn’t supported in call to 'fetchTree'" ).atPos(pos).debugThrow(); + // HACK: When using `fetchGit`, locking with only the hash should happen + // as we don't care about flake shenanigans about `lastModified` + if (type == "git" && attrs.contains("narHash")) { + using namespace std::literals::string_literals; + attrs["type"] = "\0git-locked"s; + } + input = fetchers::Input::fromAttrs(std::move(attrs)); } else { auto url = state.coerceToString(pos, *args[0], context, diff --git a/lix/libfetchers/builtin-fetchers.hh b/lix/libfetchers/builtin-fetchers.hh index d3be7f7f2..d1389b8ba 100644 --- a/lix/libfetchers/builtin-fetchers.hh +++ b/lix/libfetchers/builtin-fetchers.hh @@ -10,6 +10,7 @@ std::unique_ptr makePathInputScheme(); std::unique_ptr makeFileInputScheme(); std::unique_ptr makeTarballInputScheme(); std::unique_ptr makeGitInputScheme(); +std::unique_ptr makeGitLockedInputScheme(); std::unique_ptr makeMercurialInputScheme(); std::unique_ptr makeGitHubInputScheme(); std::unique_ptr makeGitLabInputScheme(); diff --git a/lix/libfetchers/fetchers.cc b/lix/libfetchers/fetchers.cc index dd8ec9436..4a76ed2dc 100644 --- a/lix/libfetchers/fetchers.cc +++ b/lix/libfetchers/fetchers.cc @@ -22,6 +22,7 @@ void initLibFetchers() registerInputScheme(makeTarballInputScheme()); registerInputScheme(makeFileInputScheme()); registerInputScheme(makeGitInputScheme()); + registerInputScheme(makeGitLockedInputScheme()); registerInputScheme(makeMercurialInputScheme()); registerInputScheme(makeGitHubInputScheme()); registerInputScheme(makeGitLabInputScheme()); diff --git a/lix/libfetchers/git.cc b/lix/libfetchers/git.cc index e4b2d51cb..b44ea997a 100644 --- a/lix/libfetchers/git.cc +++ b/lix/libfetchers/git.cc @@ -823,4 +823,22 @@ std::unique_ptr makeGitInputScheme() return std::make_unique(); } +struct GitLockedInputScheme : GitInputScheme { + + std::string schemeType() const override { + using namespace std::literals::string_literals; + return "\0git-locked"s; + } + + bool hasAllInfo(const Input & input) const override { + return true; + } + +}; + +std::unique_ptr makeGitLockedInputScheme() +{ + return std::make_unique(); +} + } diff --git a/tests/functional/fetchGitLocked.sh b/tests/functional/fetchGitLocked.sh new file mode 100644 index 000000000..2e405a7a1 --- /dev/null +++ b/tests/functional/fetchGitLocked.sh @@ -0,0 +1,41 @@ +source common.sh + +requireGit + +clearStore + +# Intentionally not in a canonical form +# See https://github.com/NixOS/nix/issues/6195 +repo=$TEST_ROOT/./git + +export _NIX_FORCE_HTTP=1 + +rm -rf "$repo" "$TEST_HOME/.cache/nix" + +mkdir "$repo" && pushd "$repo" + +git init --initial-branch=main +git config user.email "foobar@example.com" +git config user.name "Foobar" + +echo utrecht >hello +touch .gitignore +git add hello .gitignore +git commit -m 'Bla1' +rev1=$(git rev-parse HEAD) +git tag -a tag1 -m tag1 + +# Compute the hash of the output +path=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath") +hash=$(nix-hash --type sha256 --base32 "$path") +narHash=$(nix-hash --to-sri --type sha256 "$hash") + +# Remove the repo, and the local cache +popd && rm -rf "$repo" "$TEST_HOME/.cache/nix" + +# The path is locked and can be fetched +path2=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"main\"; rev=\"$rev1\"; narHash = \"$narHash\"; })") +[[ "$path" = "$path2" ]] + +# When no narHash is present the fetching fails +! nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"main\"; rev=\"$rev1\"; })" diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8dd142ccf..d682462c6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -76,6 +76,7 @@ functional_tests_scripts = [ 'tarball.sh', 'fetchers.sh', 'fetchGit.sh', + 'fetchGitLocked.sh', 'fetchurl.sh', 'fetchPath.sh', 'fetchTree-file.sh',