From 663187219aea93f69df9648fb2e96190e934ed98 Mon Sep 17 00:00:00 2001 From: regnat Date: Wed, 7 Jul 2021 14:29:58 +0200 Subject: [PATCH] Try and help if a file is missing at eval time When evaluating a local git flake, if a file a missing at eval-time but present in the source directory, tell the user that he probably forgot to check it out Fix #4507 --- src/libcmd/installables.cc | 49 +++++++++++++++++++++++++------------ tests/flake-missing-file.sh | 39 +++++++++++++++++++++++++++++ tests/local.mk | 1 + 3 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 tests/flake-missing-file.sh diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5758b52ad..2bf61c4df 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -528,26 +528,45 @@ std::tuple InstallableF auto cache = openEvalCache(*state, lockedFlake); auto root = cache->getRoot(); - for (auto & attrPath : getActualAttrPaths()) { - auto attr = root->findAlongAttrPath( - parseAttrPath(*state, attrPath), - true - ); + try { + for (auto & attrPath : getActualAttrPaths()) { + auto attr = root->findAlongAttrPath( + parseAttrPath(*state, attrPath), + true + ); - if (!attr) continue; + if (!attr) continue; - if (!attr->isDerivation()) - throw Error("flake output attribute '%s' is not a derivation", attrPath); + if (!attr->isDerivation()) + throw Error("flake output attribute '%s' is not a derivation", attrPath); - auto drvPath = attr->forceDerivation(); + auto drvPath = attr->forceDerivation(); - auto drvInfo = DerivationInfo{ - std::move(drvPath), - state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()), - attr->getAttr(state->sOutputName)->getString() - }; + auto drvInfo = DerivationInfo{ + std::move(drvPath), + state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()), + attr->getAttr(state->sOutputName)->getString() + }; - return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; + return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; + } + } catch (FileError & e) { + if (e.errNo != ENOENT) throw; + if (!state->store->isInStore(e.path)) throw; + auto resolvedInput = lockedFlake->flake.resolvedRef.input; + auto [sourceStorePath, relPath] = state->store->toStorePath(e.path); + if (sourceStorePath != lockedFlake->flake.lockedRef.input.computeStorePath(*state->store)) throw; + if (fetchers::maybeGetStrAttr(resolvedInput.attrs, "type").value_or("") != "git") throw; + if (auto repositoryPath = resolvedInput.getSourcePath()) { + auto sourcePath = *repositoryPath + "/" + relPath; + if (pathExists(sourcePath)) + e.addTrace( + noPos, + "The file is present in '%s'. " + "Did you forget to track it in Git?", + *repositoryPath); + } + throw; } throw Error("flake '%s' does not provide attribute %s", diff --git a/tests/flake-missing-file.sh b/tests/flake-missing-file.sh new file mode 100644 index 000000000..7f5a21a5d --- /dev/null +++ b/tests/flake-missing-file.sh @@ -0,0 +1,39 @@ +source common.sh + +if [[ -z $(type -p git) ]]; then + echo "Git not installed; skipping flake tests" + exit 99 +fi + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config + +repo=$TEST_ROOT/flake + +rm -rf $repo $repo.tmp +mkdir $repo +git -C $repo init +git -C $repo config user.email "foobar@example.com" +git -C $repo config user.name "Foobar" + +cat > $repo/flake.nix <