aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/rl-next/nix-flake-update-ux.md17
-rw-r--r--src/libcmd/command.hh6
-rw-r--r--src/libcmd/installables.cc22
-rw-r--r--src/libexpr/flake/flake.cc15
-rw-r--r--src/libfetchers/fetchers.hh2
-rw-r--r--src/libutil/args.cc13
-rw-r--r--src/libutil/args.hh14
-rw-r--r--src/nix/flake-lock.md48
-rw-r--r--src/nix/flake-update.md64
-rw-r--r--src/nix/flake.cc33
-rw-r--r--tests/functional/completions.sh11
-rw-r--r--tests/functional/flakes/circular.sh3
-rw-r--r--tests/functional/flakes/flakes.sh16
-rw-r--r--tests/functional/flakes/follow-paths.sh4
14 files changed, 175 insertions, 93 deletions
diff --git a/doc/manual/rl-next/nix-flake-update-ux.md b/doc/manual/rl-next/nix-flake-update-ux.md
new file mode 100644
index 000000000..f0f42cda8
--- /dev/null
+++ b/doc/manual/rl-next/nix-flake-update-ux.md
@@ -0,0 +1,17 @@
+---
+synposis: "`Overhaul `nix flake update` and `nix flake lock` UX"
+prs: 8817
+---
+
+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`.
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index dafc0db3b..120c832ac 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -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(
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 8afbbd51a..dca17555a 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -28,7 +28,7 @@
namespace nix {
-static void completeFlakeInputPath(
+void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
const std::vector<FlakeRef> & flakeRefs,
@@ -47,13 +47,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.",
.category = category,
@@ -86,19 +79,6 @@ MixFlakeOptions::MixFlakeOptions()
});
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`.",
.category = category,
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index d96f2b22e..2c51f8752 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -451,8 +451,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);
@@ -476,9 +476,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 =
@@ -620,7 +619,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();
@@ -655,14 +654,14 @@ LockedFlake lockFlake(
bool lockFileExists = pathExists(outputLockFilePath);
+ auto s = chomp(diff);
if (lockFileExists) {
- auto s = chomp(diff);
if (s.empty())
warn("updating lock file '%s'", outputLockFilePath);
else
warn("updating lock file '%s':\n%s", outputLockFilePath, s);
} else
- warn("creating lock file '%s'", outputLockFilePath);
+ warn("creating lock file '%s':\n%s", outputLockFilePath, s);
std::optional<std::string> commitMessage = std::nullopt;
diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh
index e1067ce1c..2bb4248be 100644
--- a/src/libfetchers/fetchers.hh
+++ b/src/libfetchers/fetchers.hh
@@ -10,7 +10,7 @@
#include <memory>
-namespace nix { class Store; class StorePath; }
+namespace nix { class Store; }
namespace nix::fetchers {
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 77069c7b9..3e39b4d7c 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -255,7 +255,18 @@ bool Args::processArgs(const Strings & args, bool finish)
}
if (!anyCompleted)
exp.handler.fun(ss);
- expectedArgs.pop_front();
+
+ /* Move the list element to the processedArgs. This is almost the same as
+ `processedArgs.push_back(expectedArgs.front()); expectedArgs.pop_front()`,
+ except that it will only adjust the next and prev pointers of the list
+ elements, meaning the actual contents don't move in memory. This is
+ critical to prevent invalidating internal pointers! */
+ processedArgs.splice(
+ processedArgs.end(),
+ expectedArgs,
+ expectedArgs.begin(),
+ ++expectedArgs.begin());
+
res = true;
}
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index c90a48ad4..77f7ff2a8 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -200,13 +200,25 @@ protected:
/**
* Queue of expected positional argument forms.
*
- * Positional arugment descriptions are inserted on the back.
+ * Positional argument descriptions are inserted on the back.
*
* As positional arguments are passed, these are popped from the
* front, until there are hopefully none left as all args that were
* expected in fact were passed.
*/
std::list<ExpectedArg> expectedArgs;
+ /**
+ * List of processed positional argument forms.
+ *
+ * All items removed from `expectedArgs` are added here. After all
+ * arguments were processed, this list should be exactly the same as
+ * `expectedArgs` was before.
+ *
+ * This list is used to extend the lifetime of the argument forms.
+ * If this is not done, some closures that reference the command
+ * itself will segfault.
+ */
+ std::list<ExpectedArg> processedArgs;
/**
* Process some positional arugments
diff --git a/src/nix/flake-lock.md b/src/nix/flake-lock.md
index 2af0ad81e..6d10258e3 100644
--- a/src/nix/flake-lock.md
+++ b/src/nix/flake-lock.md
@@ -2,37 +2,39 @@ 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
- * Updated 'nix': 'github:NixOS/nix/9fab14adbc3810d5cc1f88672fde1eee4358405c' -> 'github:NixOS/nix/8927cba62f5afb33b01016d5c4f7f8b7d0adde3c'
- * Updated 'nixpkgs': 'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' -> 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293'
+ # 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 subdirectory, write `./another`
+ > instead of `another`.
+ > Otherwise Nix will try to 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)
)""
diff --git a/src/nix/flake-update.md b/src/nix/flake-update.md
index 8c6042d94..63df3b12a 100644
--- a/src/nix/flake-update.md
+++ b/src/nix/flake-update.md
@@ -2,33 +2,57 @@ 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 --commit-lock-file
- * Updated 'nix': 'github:NixOS/nix/9fab14adbc3810d5cc1f88672fde1eee4358405c' -> 'github:NixOS/nix/8927cba62f5afb33b01016d5c4f7f8b7d0adde3c'
- * Updated 'nixpkgs': 'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' -> 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293'
- …
- warning: committed new revision '158bcbd9d6cc08ab859c0810186c1beebc982aad'
+ # nix flake update
+ warning: updating lock file '/home/myself/repos/testflake/flake.lock':
+ • Updated input 'nix':
+ 'github:NixOS/nix/9fab14adbc3810d5cc1f88672fde1eee4358405c' (2023-06-28)
+ → 'github:NixOS/nix/8927cba62f5afb33b01016d5c4f7f8b7d0adde3c' (2023-07-11)
+ • Updated input 'nixpkgs':
+ 'github:NixOS/nixpkgs/3d2d8f281a27d466fa54b469b5993f7dde198375' (2023-06-30)
+ → 'github:NixOS/nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293' (2023-07-05)
```
-# 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 subdirectory, write `./another`
+ > instead of `another`.
+ > Otherwise Nix will try to 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.
)""
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index dde4b43bd..7b5d8096a 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -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:
@@ -63,6 +65,8 @@ public:
struct CmdFlakeUpdate : FlakeCommand
{
+public:
+
std::string description() override
{
return "update flake lock file";
@@ -70,9 +74,31 @@ struct CmdFlakeUpdate : FlakeCommand
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");
}
@@ -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;
diff --git a/tests/functional/completions.sh b/tests/functional/completions.sh
index 7c1e4b287..d3d5bbd48 100644
--- a/tests/functional/completions.sh
+++ b/tests/functional/completions.sh
@@ -44,15 +44,18 @@ EOF
# Input override completion
[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --override-input '')" == $'normal\na\t' ]]
[[ "$(NIX_GET_COMPLETIONS=5 nix flake show ./foo --override-input '')" == $'normal\na\t' ]]
+cd ./foo
+[[ "$(NIX_GET_COMPLETIONS=3 nix flake update '')" == $'normal\na\t' ]]
+cd ..
+[[ "$(NIX_GET_COMPLETIONS=5 nix flake update --flake './foo' '')" == $'normal\na\t' ]]
## With multiple input flakes
[[ "$(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"
diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh
index 09cd02edf..d3bb8e8a3 100644
--- a/tests/functional/flakes/circular.sh
+++ b/tests/functional/flakes/circular.sh
@@ -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
diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh
index 7781eb51b..dfa088960 100644
--- a/tests/functional/flakes/flakes.sh
+++ b/tests/functional/flakes/flakes.sh
@@ -302,8 +302,8 @@ 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
-[[ ! -z $(git -C $flake3Dir diff master || echo failed) ]]
+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
git -C $flake3Dir checkout -b removeXyzzy
@@ -439,9 +439,9 @@ cat > $flake3Dir/flake.nix <<EOF
}
EOF
-nix flake update $flake3Dir
-[[ $(jq -c .nodes.flake2.inputs.flake1 $flake3Dir/flake.lock) =~ '["foo"]' ]]
-[[ $(jq .nodes.foo.locked.url $flake3Dir/flake.lock) =~ flake7 ]]
+nix flake update --flake "$flake3Dir"
+[[ $(jq -c .nodes.flake2.inputs.flake1 "$flake3Dir/flake.lock") =~ '["foo"]' ]]
+[[ $(jq .nodes.foo.locked.url "$flake3Dir/flake.lock") =~ flake7 ]]
# Test git+file with bare repo.
rm -rf $flakeGitBare
@@ -478,12 +478,12 @@ nix flake lock $flake3Dir --override-input flake2/flake1 flake1
nix flake lock $flake3Dir --override-input flake2/flake1 flake1/master/$hash1
[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash1 ]]
-# Test --update-input.
nix flake lock $flake3Dir
[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) = $hash1 ]]
-nix flake lock $flake3Dir --update-input flake2/flake1
-[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]]
+# Test updating an individual input of a flake lockfile.
+nix flake update flake2/flake1 --flake "$flake3Dir"
+[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]]
# Test 'nix flake metadata --json'.
nix flake metadata $flake3Dir --json | jq .
diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh
index dc97027ac..efc5f95d8 100644
--- a/tests/functional/flakes/follow-paths.sh
+++ b/tests/functional/flakes/follow-paths.sh
@@ -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
@@ -228,5 +228,5 @@ 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"