diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 08d82522a..50f904e91 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -116,7 +116,11 @@ MixFlakeOptions::MixFlakeOptions() .labels = {"input-path"}, .handler = {[&](std::string s) { warn("'--update-input' is a deprecated alias for 'flake update' and will be removed in a future version."); - lockFlags.inputUpdates.insert(flake::parseInputAttrPath(s)); + auto path = flake::NonEmptyInputAttrPath::parse(s); + if (!path) + throw UsageError( + "--update-input was passed a zero-length input path, which would refer to the flake itself, not an input"); + lockFlags.inputUpdates.insert(*path); }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { completeFlakeInputAttrPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index f7c4a196d..7fd5e0c06 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -617,7 +617,7 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, updatesUsed.insert(inputAttrPath); - if (oldNode && !lockFlags.inputUpdates.count(inputAttrPath)) + if (oldNode && !lockFlags.inputUpdates.count(nonEmptyInputAttrPath)) if (auto oldLock2 = get(oldNode->inputs, id)) if (auto oldLock3 = std::get_if<0>(&*oldLock2)) oldLock = *oldLock3; @@ -636,10 +636,10 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, /* If we have this input in updateInputs, then we must fetch the flake to update it. */ - auto lb = lockFlags.inputUpdates.lower_bound(inputAttrPath); + auto lb = lockFlags.inputUpdates.lower_bound(nonEmptyInputAttrPath); - auto mustRefetch = lb != lockFlags.inputUpdates.end() && lb->size() > inputAttrPath.size() - && std::equal(inputAttrPath.begin(), inputAttrPath.end(), lb->begin()); + auto mustRefetch = lb != lockFlags.inputUpdates.end() && lb->get().size() > inputAttrPath.size() + && std::equal(inputAttrPath.begin(), inputAttrPath.end(), lb->get().begin()); FlakeInputs fakeInputs; diff --git a/src/libflake/include/nix/flake/flake.hh b/src/libflake/include/nix/flake/flake.hh index 3a1a09f22..3bee6556f 100644 --- a/src/libflake/include/nix/flake/flake.hh +++ b/src/libflake/include/nix/flake/flake.hh @@ -211,7 +211,7 @@ struct LockFlags * Flake inputs to be updated. This means that any existing lock * for those inputs will be ignored. */ - std::set inputUpdates; + std::set inputUpdates; }; LockedFlake diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5324e0121..688aa77e0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -90,9 +90,12 @@ public: .optional = true, .handler = {[&](std::vector inputsToUpdate) { for (const auto & inputToUpdate : inputsToUpdate) { - InputAttrPath inputAttrPath; + std::optional inputAttrPath; try { - inputAttrPath = flake::parseInputAttrPath(inputToUpdate); + inputAttrPath = flake::NonEmptyInputAttrPath::parse(inputToUpdate); + if (!inputAttrPath) + throw UsageError( + "input path to be updated cannot be zero-length; it would refer to the flake itself, not an input"); } catch (Error & e) { warn( "Invalid flake input '%s'. To update a specific flake, use 'nix flake update --flake %s' instead.", @@ -100,11 +103,11 @@ public: inputToUpdate); throw e; } - if (lockFlags.inputUpdates.contains(inputAttrPath)) + if (lockFlags.inputUpdates.contains(*inputAttrPath)) warn( "Input '%s' was specified multiple times. You may have done this by accident.", - printInputAttrPath(inputAttrPath)); - lockFlags.inputUpdates.insert(inputAttrPath); + printInputAttrPath(*inputAttrPath)); + lockFlags.inputUpdates.insert(*inputAttrPath); } }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index c7f0771f9..85357bf2a 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -398,6 +398,9 @@ nix flake lock "$flake3Dir" --override-input flake2/flake1 flake1/master/"$hash1 # Test that --override-input with empty input path is rejected (issue #14816). expectStderr 1 nix flake lock "$flake3Dir" --override-input '' . | grepQuiet "input path cannot be empty" +# Test that deprecated --update-input with empty input path is rejected. +expectStderr 1 nix flake lock "$flake3Dir" --update-input '' | grepQuiet -- "--update-input was passed a zero-length input path, which would refer to the flake itself, not an input" + # Test --update-input. nix flake lock "$flake3Dir" [[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") = "$hash1" ]] @@ -405,6 +408,9 @@ nix flake lock "$flake3Dir" nix flake update flake2/flake1 --flake "$flake3Dir" [[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]] +# Test that 'nix flake update' with empty input path is rejected. +expectStderr 1 nix flake update '' --flake "$flake3Dir" | grepQuiet -- "input path to be updated cannot be zero-length; it would refer to the flake itself, not an input" + # Test updating multiple inputs. nix flake lock "$flake3Dir" --override-input flake1 flake1/master/"$hash1" nix flake lock "$flake3Dir" --override-input flake2/flake1 flake1/master/"$hash1"