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.
This commit is contained in:
Eelco Dolstra 2020-01-29 23:12:58 +01:00
parent 88b44b1e94
commit b9fb372075
6 changed files with 76 additions and 15 deletions

View file

@ -394,7 +394,13 @@ LockedFlake lockFlake(
continue; 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) { if (oldLock != oldLocks.inputs.end() && oldLock->second.originalRef == input.ref && !hasOverride) {
/* Copy the input from the old lock file if its /* Copy the input from the old lock file if its
@ -402,22 +408,39 @@ LockedFlake lockFlake(
from a higher level flake. */ from a higher level flake. */
newLocks.inputs.insert_or_assign(id, oldLock->second); newLocks.inputs.insert_or_assign(id, oldLock->second);
/* However there may be new overrides on the /* If we have an --update-input flag for an input
inputs of this flake, so we need to check those of this input, then we must fetch the flake to
(without fetching this flake - we need to be to update it. */
lazy). */ auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
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);
} 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; FlakeInputs fakeInputs;
for (auto & i : oldLock->second.inputs) { for (auto & i : oldLock->second.inputs)
fakeInputs.emplace(i.first, FlakeInput { fakeInputs.emplace(i.first, FlakeInput { .ref = i.second.originalRef });
.ref = i.second.originalRef
});
}
updateLocks(fakeInputs, updateLocks(fakeInputs,
oldLock->second, oldLock->second,
newLocks.inputs.find(id)->second, newLocks.inputs.find(id)->second,
inputPath); inputPath);
}
} else { } else {
/* We need to update/create a new lock file /* We need to update/create a new lock file

View file

@ -78,7 +78,12 @@ struct LockFlags
allowed. */ allowed. */
bool allowMutable = true; bool allowMutable = true;
/* Flake inputs to be overriden. */
std::map<InputPath, FlakeRef> inputOverrides; std::map<InputPath, FlakeRef> inputOverrides;
/* Flake inputs to be updated. This means that any existing lock
for those inputs will be ignored. */
std::set<InputPath> inputUpdates;
}; };
LockedFlake lockFlake( LockedFlake lockFlake(

View file

@ -72,6 +72,22 @@ std::optional<LockedInput *> LockedInputs::findInput(const InputPath & path)
return (LockedInput *) pos; 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 nlohmann::json LockFile::toJson() const
{ {
auto json = LockedInputs::toJson(); auto json = LockedInputs::toJson();

View file

@ -27,6 +27,8 @@ struct LockedInputs
bool isImmutable() const; bool isImmutable() const;
std::optional<LockedInput *> findInput(const InputPath & path); std::optional<LockedInput *> findInput(const InputPath & path);
void removeInput(const InputPath & path);
}; };
/* Lock file information about a flake input. */ /* Lock file information about a flake input. */

View file

@ -39,6 +39,14 @@ MixFlakeOptions::MixFlakeOptions()
.description("don't use flake registries") .description("don't use flake registries")
.set(&lockFlags.useRegistries, false); .set(&lockFlags.useRegistries, false);
mkFlag()
.longName("update-input")
.description("update a specific flake input")
.label("input-path")
.handler([&](std::vector<std::string> ss) {
lockFlags.inputUpdates.insert(flake::parseInputPath(ss[0]));
});
mkFlag() mkFlag()
.longName("override-input") .longName("override-input")
.description("override a specific flake input (e.g. 'dwarffs/nixpkgs')") .description("override a specific flake input (e.g. 'dwarffs/nixpkgs')")

View file

@ -610,3 +610,10 @@ nix flake update $flake3Dir --override-input flake2/flake1 flake1
nix flake update $flake3Dir --override-input flake2/flake1 flake1/master/$hash1 nix flake update $flake3Dir --override-input flake2/flake1 flake1/master/$hash1
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash1 ]] [[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash1 ]]
# Test --update-input.
nix flake update $flake3Dir
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash1 ]]
nix flake update $flake3Dir --update-input flake2/flake1
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash2 ]]