fetchers: don't consider a path locked if a rev is specified

It's possible to put a path into the store in pure mode by pretending
it's locked like this:

    $ echo 'lalala' > testfile
    $ nix eval --expr '(builtins.fetchTree { path = "/home/ma27/testfile"; rev = "0000000000000000000000000000000000000000"; type = "path"; })'
    { lastModified = 1723656303; lastModifiedDate = "20240814172503"; narHash = "sha256-hOMY06A0ohaaCLwnhpZIMoAqi/8kG2vk30NRiqi0dfc="; outPath = "/nix/store/lhfz259iipmv9ky995rml8018jvriynh-source"; rev = "0000000000000000000000000000000000000000"; shortRev = "0000000"; }
    $ cat /nix/store/lhfz259iipmv9ky995rml8018jvriynh-source
    lalala

There was a fix in CppNix[1], but Puck noted that it's breaking
backwards-compatibility because

> [...] a github fetch with a fully specified rev is no longer considered "locked"
> this is for "purity" reasons, but it breaks any existing flake.nix files

I tried a way smaller correctness fix here:

* Each scheme can denote whether a `rev` is enough to consider itself
  locked.
* If a `rev` is given and the scheme is OK with just a `rev` to be
  locked, the input is marked as locked.

For `path` this is not the case anymore, i.e. it requires a NAR hash to
be locked down.

[1] https://github.com/nixos/nix/commit/071dd2b3a4e6c0b2106f1b6f14ec26e153d97446

Change-Id: Ibbbf4733c82bcfa1c24dfe099a896d8aaecd81cc
This commit is contained in:
Maximilian Bosch
2025-03-20 13:17:34 +00:00
parent 2491b7cc21
commit 62fb98ae55
7 changed files with 42 additions and 1 deletions
+23
View File
@@ -0,0 +1,23 @@
---
synopsis: Don't consider a path with a specified rev to be `locked`
issues: []
cls: [2064]
category: Fixes
credits: [ma27]
---
Until now it was allowed to do e.g.
$ echo 'lalala' > testfile
$ nix eval --expr '(builtins.fetchTree { path = "/home/ma27/testfile"; rev = "0000000000000000000000000000000000000000"; type = "path"; })'
{ lastModified = 1723656303; lastModifiedDate = "20240814172503"; narHash = "sha256-hOMY06A0ohaaCLwnhpZIMoAqi/8kG2vk30NRiqi0dfc="; outPath = "/nix/store/lhfz259iipmv9ky995rml8018jvriynh-source"; rev = "0000000000000000000000000000000000000000"; shortRev = "0000000"; }
$ cat /nix/store/lhfz259iipmv9ky995rml8018jvriynh-source
lalala
because any kind of input with a `rev` specified is considered to be locked.
With this change, inputs of type `path`, `indirect` and `tarball` are no longer
considered locked with a rev, but no hash specified.
This behavior was changed in
[CppNix 2.21 as well](https://github.com/nixos/nix/commit/071dd2b3a4e6c0b2106f1b6f14ec26e153d97446) as well.
+1 -1
View File
@@ -39,7 +39,7 @@ static void fixupInput(Input & input)
// Check common attributes.
input.getType();
input.getRef();
if (input.getRev())
if (input.scheme && input.scheme->isLockedByRev() && input.getRev())
input.locked = true;
input.getRevCount();
input.getLastModified();
+7
View File
@@ -165,6 +165,13 @@ struct InputScheme
virtual kj::Promise<Result<std::pair<StorePath, Input>>>
fetch(ref<Store> store, const Input & input) = 0;
/*
* By default, libfetchers considers inputs as locked if a `rev`
* is specified. This however doesn't make any sense for `path` inputs,
* so schemes can indicate that a `rev` on its own is not sufficient.
*/
virtual bool isLockedByRev() const { return true; }
protected:
void emplaceURLQueryIntoAttrs(
const ParsedURL & parsedURL,
+2
View File
@@ -86,6 +86,8 @@ struct IndirectInputScheme : InputScheme
return url;
}
bool isLockedByRev() const override { return false; }
bool hasAllInfo(const Input & input) const override
{
return false;
+2
View File
@@ -56,6 +56,8 @@ struct PathInputScheme : InputScheme
return input;
}
bool isLockedByRev() const override { return false; }
ParsedURL toURL(const Input & input) const override
{
auto query = attrsToQuery(input.attrs);
+2
View File
@@ -253,6 +253,8 @@ struct CurlInputScheme : InputScheme
return url;
}
bool isLockedByRev() const override { return false; }
bool hasAllInfo(const Input & input) const override
{
return true;
+5
View File
@@ -89,3 +89,8 @@ testFetchTreeError \
testFetchTreeError \
"{ type = \"hg\"; url = \"https://forge.tld/owner/repo\"; ref = \",\"; }" \
"invalid Mercurial branch/tag name ','"
echo 'hello lix' > testfile
output="$(nix eval --expr '(builtins.fetchTree { path = "'"$(pwd)"'/testfile"; rev = "0000000000000000000000000000000000000000"; type = "path"; })' 2>&1)" && status=0 || status=$?
[ "$status" -eq 1 ]
grepQuiet "error: in pure evaluation mode, 'fetchTree' requires a locked input" <<<"$output"