From b9fb3720750a24d8b2fbe6b1491696d6a51dcff3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Jan 2020 23:12:58 +0100 Subject: Add --update-input flag to update a specific flake input Typical usage: $ nix flake update ~/Misc/eelco-configurations/hagbard --update-input nixpkgs to update the 'nixpkgs' input of a flake while leaving every other input unchanged. The argument is an input path, so you can do e.g. '--update-input dwarffs/nixpkgs' to update an input of an input. Fixes #2928. --- src/libexpr/flake/flake.cc | 53 +++++++++++++++++++++++++++++++------------ src/libexpr/flake/flake.hh | 5 ++++ src/libexpr/flake/lockfile.cc | 16 +++++++++++++ src/libexpr/flake/lockfile.hh | 2 ++ src/nix/installables.cc | 8 +++++++ 5 files changed, 69 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index c752eeedc..a8f05efb6 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -394,7 +394,13 @@ LockedFlake lockFlake( continue; } - auto oldLock = oldLocks.inputs.find(id); + /* Do we have an entry in the existing lock file? And + we don't have a --update-input flag for this + input? */ + auto oldLock = + lockFlags.inputUpdates.count(inputPath) + ? oldLocks.inputs.end() + : oldLocks.inputs.find(id); if (oldLock != oldLocks.inputs.end() && oldLock->second.originalRef == input.ref && !hasOverride) { /* Copy the input from the old lock file if its @@ -402,22 +408,39 @@ LockedFlake lockFlake( from a higher level flake. */ newLocks.inputs.insert_or_assign(id, oldLock->second); - /* However there may be new overrides on the - inputs of this flake, so we need to check those - (without fetching this flake - we need to be - lazy). */ - FlakeInputs fakeInputs; + /* If we have an --update-input flag for an input + of this input, then we must fetch the flake to + to update it. */ + auto lb = lockFlags.inputUpdates.lower_bound(inputPath); - for (auto & i : oldLock->second.inputs) { - fakeInputs.emplace(i.first, FlakeInput { - .ref = i.second.originalRef - }); - } + auto hasChildUpdate = + lb != lockFlags.inputUpdates.end() + && lb->size() > inputPath.size() + && std::equal(inputPath.begin(), inputPath.end(), lb->begin()); + + if (hasChildUpdate) { + auto inputFlake = getFlake(state, oldLock->second.ref, false, flakeCache); + + updateLocks(inputFlake.inputs, + (const LockedInputs &) oldLock->second, + newLocks.inputs.find(id)->second, + inputPath); - updateLocks(fakeInputs, - oldLock->second, - newLocks.inputs.find(id)->second, - inputPath); + } else { + /* No need to fetch this flake, we can be + lazy. However there may be new overrides on + the inputs of this flake, so we need to + check those. */ + FlakeInputs fakeInputs; + + for (auto & i : oldLock->second.inputs) + fakeInputs.emplace(i.first, FlakeInput { .ref = i.second.originalRef }); + + updateLocks(fakeInputs, + oldLock->second, + newLocks.inputs.find(id)->second, + inputPath); + } } else { /* We need to update/create a new lock file diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 355e22adc..fd03faf13 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -78,7 +78,12 @@ struct LockFlags allowed. */ bool allowMutable = true; + /* Flake inputs to be overriden. */ std::map inputOverrides; + + /* Flake inputs to be updated. This means that any existing lock + for those inputs will be ignored. */ + std::set inputUpdates; }; LockedFlake lockFlake( diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index b92ffb70e..edb7628fd 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -72,6 +72,22 @@ std::optional LockedInputs::findInput(const InputPath & path) return (LockedInput *) pos; } +void LockedInputs::removeInput(const InputPath & path) +{ + assert(!path.empty()); + + LockedInputs * pos = this; + + for (size_t n = 0; n < path.size(); n++) { + auto i = pos->inputs.find(path[n]); + if (i == pos->inputs.end()) return; + if (n + 1 == path.size()) + pos->inputs.erase(i); + else + pos = &i->second; + } +} + nlohmann::json LockFile::toJson() const { auto json = LockedInputs::toJson(); diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh index 626c7d9e0..ff1181d13 100644 --- a/src/libexpr/flake/lockfile.hh +++ b/src/libexpr/flake/lockfile.hh @@ -27,6 +27,8 @@ struct LockedInputs bool isImmutable() const; std::optional findInput(const InputPath & path); + + void removeInput(const InputPath & path); }; /* Lock file information about a flake input. */ diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 24eb739f5..77a43ef2a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -39,6 +39,14 @@ MixFlakeOptions::MixFlakeOptions() .description("don't use flake registries") .set(&lockFlags.useRegistries, false); + mkFlag() + .longName("update-input") + .description("update a specific flake input") + .label("input-path") + .handler([&](std::vector ss) { + lockFlags.inputUpdates.insert(flake::parseInputPath(ss[0])); + }); + mkFlag() .longName("override-input") .description("override a specific flake input (e.g. 'dwarffs/nixpkgs')") -- cgit v1.2.3