1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-23 17:31:08 +01:00

libflake: reject empty paths in inputUpdates

An empty path refers to the flake itself, not an input. Apply the same
type safety to inputUpdates as inputOverrides.

The deprecated --update-input flag (deprecated since Nix 2.4) and the
modern 'nix flake update' command now properly reject empty paths.

Includes functional tests for both commands.
This commit is contained in:
Robert Hensing 2025-12-20 01:33:23 +01:00
parent f7fc24c973
commit bec436c0b1
5 changed files with 24 additions and 11 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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<InputAttrPath> inputUpdates;
std::set<NonEmptyInputAttrPath> inputUpdates;
};
LockedFlake

View file

@ -90,9 +90,12 @@ public:
.optional = true,
.handler = {[&](std::vector<std::string> inputsToUpdate) {
for (const auto & inputToUpdate : inputsToUpdate) {
InputAttrPath inputAttrPath;
std::optional<NonEmptyInputAttrPath> 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) {

View file

@ -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"