Skip to content

Commit

Permalink
Overhaul nix flake update and lock commands
Browse files Browse the repository at this point in the history
Closes NixOS#5110
  • Loading branch information
iFreilicht committed Oct 25, 2023
1 parent 09ac599 commit e12a28b
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 78 deletions.
14 changes: 14 additions & 0 deletions doc/manual/src/release-notes/rl-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,17 @@
- `nix-shell` shebang lines now support single-quoted arguments.

- `builtins.fetchTree` is now marked as stable.


- The interface for creating and updating lock files has been overhauled:

- [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
It will *never* update existing inputs.

- [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
- Passing no arguments will update all inputs of the current flake, just like it already did.
- Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
- To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.

- The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
They are superceded by `nix flake update`.
6 changes: 6 additions & 0 deletions src/libcmd/command.hh
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ struct MixEnvironment : virtual Args {
void setEnviron();
};

void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix);

void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix);

void completeFlakeRefWithFragment(
Expand Down
22 changes: 1 addition & 21 deletions src/libcmd/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace nix {

static void completeFlakeInputPath(
void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
const std::vector<FlakeRef> & flakeRefs,
Expand All @@ -46,13 +46,6 @@ MixFlakeOptions::MixFlakeOptions()
{
auto category = "Common flake-related options";

addFlag({
.longName = "recreate-lock-file",
.description = "Recreate the flake's lock file from scratch.",
.category = category,
.handler = {&lockFlags.recreateLockFile, true}
});

addFlag({
.longName = "no-update-lock-file",
.description = "Do not allow any updates to the flake's lock file.",
Expand Down Expand Up @@ -85,19 +78,6 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {&lockFlags.commitLockFile, true}
});

addFlag({
.longName = "update-input",
.description = "Update a specific flake input (ignoring its previous entry in the lock file).",
.category = category,
.labels = {"input-path"},
.handler = {[&](std::string s) {
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
}}
});

addFlag({
.longName = "override-input",
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.",
Expand Down
11 changes: 5 additions & 6 deletions src/libexpr/flake/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,8 @@ LockedFlake lockFlake(

assert(input.ref);

/* Do we have an entry in the existing lock file? And we
don't have a --update-input flag for this input? */
/* Do we have an entry in the existing lock file?
And the input is not in updateInputs? */
std::shared_ptr<LockedNode> oldLock;

updatesUsed.insert(inputPath);
Expand All @@ -472,9 +472,8 @@ LockedFlake lockFlake(

node->inputs.insert_or_assign(id, childNode);

/* If we have an --update-input flag for an input
of this input, then we must fetch the flake to
update it. */
/* If we have this input in updateInputs, then we
must fetch the flake to update it. */
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);

auto mustRefetch =
Expand Down Expand Up @@ -616,7 +615,7 @@ LockedFlake lockFlake(

for (auto & i : lockFlags.inputUpdates)
if (!updatesUsed.count(i))
warn("the flag '--update-input %s' does not match any input", printInputPath(i));
warn("'%s' does not match any input of this flake", printInputPath(i));

/* Check 'follows' inputs. */
newLockFile.check();
Expand Down
38 changes: 17 additions & 21 deletions src/nix/flake-lock.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,36 @@ R""(

# Examples

* Update the `nixpkgs` and `nix` inputs of the flake in the current
directory:
* Create the lock file for the flake in the current directory:

```console
# nix flake lock --update-input nixpkgs --update-input nix
# nix flake lock
warning: creating lock file '/home/myself/repos/testflake/flake.lock':
• Added input 'nix':
'github:NixOS/nix/9fab14adbc3810d5cc1f88672fde1eee4358405c' (2023-06-28)
• Added input 'nixpkgs':
'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
```

# Description

This command updates the lock file of a flake (`flake.lock`) so that
it contains a lock for every flake input specified in
`flake.nix`. Existing lock file entries are not updated unless
required by a flag such as `--update-input`.
* Add missing inputs to the lock file for a flake in a different directory:

Note that every command that operates on a flake will also update the
lock file if needed, and supports the same flags. Therefore,
```console
# nix flake lock ~/repos/another
warning: updating lock file '/home/myself/repos/another/flake.lock':
• Added input 'nixpkgs':
'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
```

```console
# nix flake lock --update-input nixpkgs
# nix build
```
Note: When trying to refer to a flake in a direct descendant directory, write `./another`
instead of `another`, else Nix will try look up the flake in the registry.

is equivalent to:
# Description

```console
# nix build --update-input nixpkgs
```
This command adds inputs to the lock file of a flake (`flake.lock`)
so that it contains a lock for every flake input specified in
`flake.nix`. Existing lock file entries are not updated.

Thus, this command is only useful if you want to update the lock file
separately from any other action such as building.
If you want to update existing lock entries, use
[`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md)

)""
50 changes: 33 additions & 17 deletions src/nix/flake-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ R""(

# Examples

* Recreate the lock file (i.e. update all inputs) and commit the new
lock file:
* Update all inputs (i.e. recreate the lock file from scratch):

```console
# nix flake update
Expand All @@ -14,26 +13,43 @@ R""(
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
→ 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293' (2023-07-05)
warning: committed new revision '158bcbd9d6cc08ab859c0810186c1beebc982aad'
```

# Description
* Update only a single input:

```console
# nix flake update nixpkgs
warning: updating lock file '/home/myself/repos/testflake/flake.lock':
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
→ 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293' (2023-07-05)
```

* Update only a single input of a flake in a different directory:

This command recreates the lock file of a flake (`flake.lock`), thus
updating the lock for every unlocked input (like `nixpkgs`) to its
current version. This is equivalent to passing `--recreate-lock-file`
to any command that operates on a flake. That is,
```console
# nix flake update nixpkgs --flake ~/repos/another
warning: updating lock file '/home/myself/repos/another/flake.lock':
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
→ 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293' (2023-07-05)

Note: When trying to refer to a flake in a direct descendant directory, write `./another`
instead of `another`, else Nix will try look up the flake in the registry.
```

# Description

```console
# nix flake update
# nix build
```
This command updates the inputs in a lock file (`flake.lock`).
**By default, all inputs are updated**. If the lock file doesn't exist
yet, it will be created. If inputs are not in the lock file yet, they will be added.

is equivalent to:
Unlike other `nix flake` commands, `nix flake update` takes a list of names of inputs
to update as its positional arguments and operates on the flake in the current directory.
You can pass a different flake-url with `--flake` to override that default.

```console
# nix build --recreate-lock-file
```
The related command [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md)
also creates lock files and adds missing inputs, but is safer as it
will never update inputs already in the lock file.

)""
33 changes: 30 additions & 3 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ using namespace nix;
using namespace nix::flake;
using json = nlohmann::json;

struct CmdFlakeUpdate;
class FlakeCommand : virtual Args, public MixFlakeOptions
{
protected:
std::string flakeUrl = ".";

public:
Expand Down Expand Up @@ -63,16 +65,40 @@ class FlakeCommand : virtual Args, public MixFlakeOptions

struct CmdFlakeUpdate : FlakeCommand
{
public:

std::string description() override
{
return "update flake lock file";
}

CmdFlakeUpdate()
{
expectedArgs.clear();
addFlag({
.longName="flake",
.description="The flake to operate on. Default is the current directory.",
.labels={"flake-url"},
.handler={&flakeUrl},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getStore(), prefix);
}}
});
expectArgs({
.label="inputs",
.optional=true,
.handler={[&](std::string inputToUpdate){
auto inputPath = flake::parseInputPath(inputToUpdate);
if (lockFlags.inputUpdates.contains(inputPath))
warn("Input '%s' was specified multiple times. You may have done this by accident.");
lockFlags.inputUpdates.insert(inputPath);
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
}}
});

/* Remove flags that don't make sense. */
removeFlag("recreate-lock-file");
removeFlag("update-input");
removeFlag("no-update-lock-file");
removeFlag("no-write-lock-file");
}
Expand All @@ -87,8 +113,9 @@ struct CmdFlakeUpdate : FlakeCommand
void run(nix::ref<nix::Store> store) override
{
settings.tarballTtl = 0;
auto updateAll = lockFlags.inputUpdates.empty();

lockFlags.recreateLockFile = true;
lockFlags.recreateLockFile = updateAll;
lockFlags.writeLockFile = true;
lockFlags.applyNixConfig = true;

Expand Down
7 changes: 3 additions & 4 deletions tests/functional/completions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,10 @@ EOF
[[ "$(NIX_GET_COMPLETIONS=5 nix build ./foo ./bar --override-input '')" == $'normal\na\t\nb\t' ]]
## With tilde expansion
[[ "$(HOME=$PWD NIX_GET_COMPLETIONS=4 nix build '~/foo' --override-input '')" == $'normal\na\t' ]]
[[ "$(HOME=$PWD NIX_GET_COMPLETIONS=5 nix flake show '~/foo' --update-input '')" == $'normal\na\t' ]]
[[ "$(HOME=$PWD NIX_GET_COMPLETIONS=4 nix run '~/foo' --update-input '')" == $'normal\na\t' ]]
[[ "$(HOME=$PWD NIX_GET_COMPLETIONS=5 nix flake update --flake '~/foo' '')" == $'normal\na\t' ]]
## Out of order
[[ "$(NIX_GET_COMPLETIONS=3 nix build --update-input '' ./foo)" == $'normal\na\t' ]]
[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --update-input '' ./bar)" == $'normal\na\t\nb\t' ]]
[[ "$(NIX_GET_COMPLETIONS=3 nix build --override-input '' '' ./foo)" == $'normal\na\t' ]]
[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --override-input '' '' ./bar)" == $'normal\na\t\nb\t' ]]

# Cli flag completion
NIX_GET_COMPLETIONS=2 nix build --log-form | grep -- "--log-format"
Expand Down
3 changes: 2 additions & 1 deletion tests/functional/flakes/circular.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ git -C $flakeB commit -a -m 'Foo'
sed -i $flakeB/flake.nix -e 's/456/789/'
git -C $flakeB commit -a -m 'Foo'

[[ $(nix eval --update-input b $flakeA#foo) = 1912 ]]
nix flake update b --flake $flakeA
[[ $(nix eval $flakeA#foo) = 1912 ]]

# Test list-inputs with circular dependencies
nix flake metadata $flakeA
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/flakes/flakes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ nix build -o "$TEST_ROOT/result" flake4#xyzzy
nix flake lock "$flake3Dir"
[[ -z $(git -C "$flake3Dir" diff master || echo failed) ]]

nix flake update "$flake3Dir" --override-flake flake2 nixpkgs
nix flake update --flake "$flake3Dir" --override-flake flake2 nixpkgs
[[ ! -z $(git -C "$flake3Dir" diff master || echo failed) ]]

# Make branch "removeXyzzy" where flake3 doesn't have xyzzy anymore
Expand Down Expand Up @@ -437,7 +437,7 @@ cat > "$flake3Dir/flake.nix" <<EOF
}
EOF

nix flake update "$flake3Dir"
nix flake update --flake "$flake3Dir"
[[ $(jq -c .nodes.flake2.inputs.flake1 "$flake3Dir/flake.lock") =~ '["foo"]' ]]
[[ $(jq .nodes.foo.locked.url "$flake3Dir/flake.lock") =~ flake7 ]]

Expand Down Expand Up @@ -480,7 +480,7 @@ nix flake lock "$flake3Dir" --override-input flake2/flake1 flake1/master/$hash1
nix flake lock "$flake3Dir"
[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") = $hash1 ]]

nix flake lock "$flake3Dir" --update-input flake2/flake1
nix flake update flake2/flake1 --flake "$flake3Dir"
[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]]

# Test 'nix flake metadata --json'.
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/flakes/follow-paths.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ git -C $flakeFollowsA add flake.nix flakeB/flake.nix \

nix flake metadata $flakeFollowsA

nix flake update $flakeFollowsA
nix flake update --flake $flakeFollowsA

nix flake lock $flakeFollowsA

Expand Down Expand Up @@ -228,7 +228,7 @@ git -C "$flakeFollowsOverloadA" add flake.nix flakeB/flake.nix \
flakeB/flakeC/flake.nix flakeB/flakeC/flakeD/flake.nix

nix flake metadata "$flakeFollowsOverloadA"
nix flake update "$flakeFollowsOverloadA"
nix flake update --flake "$flakeFollowsOverloadA"
nix flake lock "$flakeFollowsOverloadA"

# Now test follow cycle detection
Expand Down

0 comments on commit e12a28b

Please sign in to comment.