aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThéophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>2023-10-31 16:19:05 +0100
committerLunaphied <lunaphied@lunaphied.me>2024-03-25 17:36:24 -0600
commit86881226b0e51c204f07385b7b57743c23e34d44 (patch)
tree737e75ee7cc6ec8932b850ff6f20adfc4e285628 /src
parentaa7653608d4d0028bb98af491aec76b2fea7f882 (diff)
Merge pull request #8817 from iFreilicht/flake-update-lock-overhaul
Overhaul `nix flake update` and `nix flake lock` UX (cherry picked from commit 12a0ae73dbb37becefa5a442eb4532ff0de9ce65) Change-Id: Iff3b4f4235ebb1948ec612036b39ab29e4ca22b2
Diffstat (limited to 'src')
-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
9 files changed, 139 insertions, 78 deletions
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;