libstore/build: fix URL guesswork for failing structuredAttrs FODs

Fixes #1175

Change-Id: I6a074f489620f6de0615858f4d0d872c6a6a6964
This commit is contained in:
ash
2026-06-20 14:31:17 +01:00
parent cf0ce785b7
commit ff920956a9
5 changed files with 84 additions and 2 deletions
+3
View File
@@ -150,6 +150,9 @@ kasimeka:
forgejo: janw4ld
github: kasimeka
keysmashes:
github: keysmashes
kfears:
display_name: KFears
forgejo: kfearsoff
@@ -0,0 +1,11 @@
---
synopsis: "Hash mismatch diagnostics now work with `structuredAttrs`"
issues: [fj#1175]
cls: [5441]
category: Fixes
credits: [keysmashes]
---
Nixpkgs fetchers like `fetchurl` now use `structuredAttrs`, which broke the
hash mismatch diagnostics added in Lix 2.91. This has been fixed and the likely
URL is now shown again.
@@ -1711,6 +1711,23 @@ try {
anyHashMismatchSeen = true;
// XXX: shameless layering violation hack that makes the hash mismatch error at least not utterly worthless
auto guessedUrl = getOr(drv->env, "urls", getOr(drv->env, "url", "(unknown)"));
if (guessedUrl == "(unknown)") {
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
auto json = *structuredAttrs;
if (json.contains("urls") && json["urls"].is_array() && !json["urls"].empty()) {
guessedUrl = "";
for (auto it = json["urls"].begin(); it != json["urls"].end(); ++it) {
if (!it->is_string()) continue;
if (guessedUrl != "") {
guessedUrl += " or ";
}
guessedUrl += it->get<std::string>();
}
} else if (json.contains("url") && json["url"].is_string()) {
guessedUrl = json["url"].get<std::string>();
}
}
}
delayedException = std::make_exception_ptr(BuildError(
"hash mismatch in fixed-output derivation '%s':\n likely URL: %s\n "
"specified: %s\n got: %s\n expected path: %s\n got path: %s",
@@ -38,4 +38,37 @@ rec {
exit 1
'';
};
x5 = mkDerivation {
name = "x5";
__structuredAttrs = true;
buildCommand = "echo $name > \${outputs[out]}";
url = "https://avian.example/owls.tar.gz";
outputHashMode = "recursive";
outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
x6 = mkDerivation {
name = "x6";
__structuredAttrs = true;
buildCommand = "echo $name > \${outputs[out]}";
urls = [ "https://avian.example/geese.tar.gz" ];
outputHashMode = "recursive";
outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
x7 = mkDerivation {
name = "x7";
__structuredAttrs = true;
buildCommand = "echo $name > \${outputs[out]}";
url = 42;
urls = 0;
outputHashMode = "recursive";
outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
x8 = mkDerivation {
name = "x8";
__structuredAttrs = true;
buildCommand = "echo $name > \${outputs[out]}";
urls = [ "https://avian.example/swan1.tar.gz" "https://avian.example/swan2.tar.gz" ];
outputHashMode = "recursive";
outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
}
@@ -1,5 +1,7 @@
import re
import pytest
from testlib.fixtures.file_helper import with_files, CopyFile
from testlib.fixtures.nix import Nix
from testlib.utils import get_global_asset
@@ -27,8 +29,7 @@ def test_url_keep_going_1(nix: Nix):
assert re.findall(r"hash mismatch in fixed-output derivation '.*-x.\.drv'", err)
assert "likely URL: " in err
assert re.findall(
r"error: build of '.*-x[1-4]\.drv\^out', '.*-x[1-4]\.drv\^out', '.*-x[1-4]\.drv\^out', '.*-x[1-4]\.drv\^out' failed",
err,
r"error: build of '.*-x[1-8]\.drv\^out'(, '.*-x[1-7]\.drv\^out'){6} failed", err
)
@@ -67,6 +68,23 @@ def test_missing_dependency_keep_going(nix: Nix):
assert re.findall(rf"hash mismatch in fixed-output derivation '.*-{f}\.drv'", err)
@with_files(_fod_files)
@pytest.mark.parametrize(
("attr", "url"),
[
("x5", "https://avian.example/owls.tar.gz"),
("x6", "https://avian.example/geese.tar.gz"),
("x7", "(unknown)"),
("x8", "https://avian.example/swan1.tar.gz or https://avian.example/swan2.tar.gz"),
],
)
def test_structured_attrs_url(nix: Nix, attr: str, url: str):
res = nix.nix([*_build_args, attr], flake=True).run().expect(1)
err = res.stderr_plain
assert err.count("error: ") == 1
assert f"likely URL: {url}" in err
@with_files({"config.nix": get_global_asset("config.nix")})
def test_build_inaccessible_build_dir(nix: Nix):
env = nix.env