libfetchers: don't treat empty/zero hashes as valid locks

treating a file with an empty or zero hash as locked is not helpful.
these are placeholdes for "hash is not known", thus treating them as
a valid lock makes them completely useless (and confusing to users).

fixes #1233

Change-Id: If42b47281e6973fc86662b973db69e26f6346f5a
This commit is contained in:
eldritch horrors
2026-07-03 11:06:57 +00:00
parent 48f7747deb
commit e9a01a5be6
4 changed files with 99 additions and 2 deletions
+10
View File
@@ -0,0 +1,10 @@
---
synopsis: "don't treat tarball fetches with empty or zero hash as locked"
cls: []
category: "Fixes"
credits: [horrors]
issues: [fj#1233]
---
Lix no longer treats tarball fetches with empty or zero hashes as locked.
All such fetches are now also affected by `tarball-ttl` as a consequence.
+8 -2
View File
@@ -308,10 +308,16 @@ static Value fetch(
// https://github.com/NixOS/nix/issues/4313
auto storePath = unpack
? state.aio
.blockOn(fetchers::downloadTarball(state.ctx.store, *url, name, (bool) expectedHash))
.blockOn(
fetchers::downloadTarball(
state.ctx.store, *url, name, expectedHash != Hash(HashType::SHA256)
)
)
.tree.storePath
: state.aio
.blockOn(fetchers::downloadFile(state.ctx.store, *url, name, (bool) expectedHash))
.blockOn(
fetchers::downloadFile(state.ctx.store, *url, name, expectedHash != Hash(HashType::SHA256))
)
.storePath;
if (expectedHash) {
@@ -0,0 +1,81 @@
from pathlib import Path
import pytest
from testlib.fixtures.nix import Nix
@pytest.mark.no_daemon
@pytest.mark.parametrize("sha", ["", "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="])
def test_locked_tarball_ttl(nix: Nix, sha: str):
files = nix.env.dirs.home
(files / "file").write_text("contents")
res = (
nix.nix(
[
"eval",
"--expr",
f'builtins.fetchurl {{ url = "file://{files}/file"; sha256 = "{sha}"; }}',
],
flake=True,
)
.run()
.expect(102)
)
assert "hash mismatch" in res.stderr_plain
assert "sha256-0bKln76n4gB3r5+Rsn6V6GUGGycL4D/1Oas7c1h4gug=" in res.stderr_plain
# empty or zero hash should count as not locked and be refetched
(files / "file").write_text("other contents")
res = (
nix.nix(
[
"eval",
*["--tarball-ttl", "0"],
"--expr",
f'builtins.fetchurl {{ url = "file://{files}/file"; sha256 = "{sha}"; }}',
],
flake=True,
)
.run()
.expect(102)
)
assert "hash mismatch" in res.stderr_plain
assert "sha256-Ql7LXhAAyVRbEF8VR8bW8D0c1LONlNRX7AMpB+i1EHk=" in res.stderr_plain
# hash-locked tarballs should *not* be refetched as long as their store path exists
(files / "file").write_text("different contents")
res = (
nix.nix(
[
"eval",
*["--tarball-ttl", "0"],
"--raw",
"--expr",
f'builtins.fetchurl {{ url = "file://{files}/file"; sha256 = "sha256-Ql7LXhAAyVRbEF8VR8bW8D0c1LONlNRX7AMpB+i1EHk="; }}',
],
flake=True,
)
.run()
.ok()
)
path = Path(res.stdout_plain)
assert path.read_text() == "other contents"
# but store paths that went away are always refetched and checked
nix.nix(["store", "delete", path], flake=True).run().ok()
res = (
nix.nix(
[
"eval",
"--expr",
f'builtins.fetchurl {{ url = "file://{files}/file"; sha256 = "sha256-Ql7LXhAAyVRbEF8VR8bW8D0c1LONlNRX7AMpB+i1EHk="; }}',
],
flake=True,
)
.run()
.expect(102)
)
assert "hash mismatch" in res.stderr_plain
assert "sha256-DKElpSdXX/9t9X/8f4IfbwQccBptZZqVy1/6JW6uFoY=" in res.stderr_plain