Skip to content

Commit a38c7eb

Browse files
committed
Print failing attribute paths in nix flake check
1 parent 7e8db2e commit a38c7eb

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

src/nix/flake.cc

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ struct CmdFlakeCheck : FlakeCommand
423423
return std::nullopt;
424424
};
425425

426-
std::vector<DerivedPath> drvPaths;
426+
std::map<DerivedPath, std::vector<AttrPath>> attrPathsByDrv;
427427

428428
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
429429
try {
@@ -621,7 +621,13 @@ struct CmdFlakeCheck : FlakeCommand
621621
.drvPath = makeConstantStorePathRef(*drvPath),
622622
.outputs = OutputsSpec::All{},
623623
};
624-
drvPaths.push_back(std::move(path));
624+
625+
// Build and store the attribute path for error reporting
626+
AttrPath attrPath;
627+
attrPath.push_back(AttrName(state->symbols.create(name)));
628+
attrPath.push_back(AttrName(attr.name));
629+
attrPath.push_back(AttrName(attr2.name));
630+
attrPathsByDrv[path].push_back(std::move(attrPath));
625631
}
626632
}
627633
}
@@ -785,7 +791,9 @@ struct CmdFlakeCheck : FlakeCommand
785791
});
786792
}
787793

788-
if (build && !drvPaths.empty()) {
794+
if (build && !attrPathsByDrv.empty()) {
795+
auto keys = std::views::keys(attrPathsByDrv);
796+
std::vector<DerivedPath> drvPaths(keys.begin(), keys.end());
789797
// TODO: This filtering of substitutable paths is a temporary workaround until
790798
// https://github.com/NixOS/nix/issues/5025 (union stores) is implemented.
791799
//
@@ -811,7 +819,28 @@ struct CmdFlakeCheck : FlakeCommand
811819
}
812820

813821
Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", toBuild.size()));
814-
store->buildPaths(toBuild);
822+
auto results = store->buildPathsWithResults(toBuild);
823+
824+
// Report build failures with attribute paths
825+
for (auto & result : results) {
826+
if (auto * failure = result.tryGetFailure()) {
827+
auto it = attrPathsByDrv.find(result.path);
828+
if (it != attrPathsByDrv.end() && !it->second.empty()) {
829+
for (auto & attrPath : it->second) {
830+
auto attrPathStr = showAttrPath(state->symbols, attrPath);
831+
reportError(Error(
832+
"failed to build attribute '%s', build of '%s' failed: %s",
833+
attrPathStr,
834+
result.path.to_string(*store),
835+
failure->errorMsg));
836+
}
837+
} else {
838+
// Derivation has no attribute path (e.g., a build dependency)
839+
reportError(
840+
Error("build of '%s' failed: %s", result.path.to_string(*store), failure->errorMsg));
841+
}
842+
}
843+
}
815844
}
816845
if (hasErrors)
817846
throw Error("some errors were encountered during the evaluation");

tests/functional/flakes/check.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,24 @@ EOF
192192
# shellcheck disable=SC2015
193193
checkRes=$(nix flake check "$flakeDir" 2>&1 && fail "nix flake check should have failed" || true)
194194
echo "$checkRes" | grepQuiet -E "builder( for .*)? failed with exit code 1"
195+
196+
# Test that attribute paths are shown in error messages
197+
cat > "$flakeDir"/flake.nix <<EOF
198+
{
199+
outputs = { self }: with import ./config.nix; {
200+
checks.${system}.failingCheck = mkDerivation {
201+
name = "failing-check";
202+
buildCommand = "echo 'This check fails'; exit 1";
203+
};
204+
checks.${system}.anotherFailingCheck = mkDerivation {
205+
name = "another-failing-check";
206+
buildCommand = "echo 'This also fails'; exit 1";
207+
};
208+
};
209+
}
210+
EOF
211+
212+
# shellcheck disable=SC2015
213+
checkRes=$(nix flake check --keep-going "$flakeDir" 2>&1 && fail "nix flake check should have failed" || true)
214+
echo "$checkRes" | grepQuiet "checks.${system}.failingCheck"
215+
echo "$checkRes" | grepQuiet "checks.${system}.anotherFailingCheck"

0 commit comments

Comments
 (0)