From e91596eb6922157aaba17a858dc52244dd0e5688 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Mon, 13 Mar 2023 21:08:52 +0100 Subject: Allow specifying alternative paths for reading/writing flake locks This allows having multiple separate lockfiles for a single project, which can be useful for testing against different versions of nixpkgs; it also allows tracking custom input overrides for remote flakes without requiring local clones of these flakes. For example, if I want to build Nix against my locally pinned nixpkgs, and have a lock file tracking this override independently of future updates to said nixpkgs: nix flake lock --output-lock-file /tmp/nix-flake.lock --override-input nixpkgs flake:nixpkgs nix build --reference-lock-file /tmp/nix-flake.lock Co-Authored-By: Will Fancher --- src/libexpr/flake/flake.cc | 31 +++++++++++++++++++------------ src/libexpr/flake/flake.hh | 6 ++++++ 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'src/libexpr/flake') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 336eb274d..64ec4bdd2 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -337,7 +337,8 @@ LockedFlake lockFlake( // FIXME: symlink attack auto oldLockFile = LockFile::read( - flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"); + lockFlags.referenceLockFilePath.value_or( + flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock")); debug("old lock file: %s", oldLockFile); @@ -619,13 +620,20 @@ LockedFlake lockFlake( debug("new lock file: %s", newLockFile); + auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"; + auto sourcePath = topRef.input.getSourcePath(); + auto outputLockFilePath = sourcePath ? std::optional{*sourcePath + "/" + relPath} : std::nullopt; + if (lockFlags.outputLockFilePath) { + outputLockFilePath = lockFlags.outputLockFilePath; + } + /* Check whether we need to / can write the new lock file. */ - if (!(newLockFile == oldLockFile)) { + if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) { auto diff = LockFile::diff(oldLockFile, newLockFile); if (lockFlags.writeLockFile) { - if (auto sourcePath = topRef.input.getSourcePath()) { + if (outputLockFilePath) { if (auto unlockedInput = newLockFile.isUnlocked()) { if (fetchSettings.warnDirty) warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput); @@ -633,25 +641,24 @@ LockedFlake lockFlake( if (!lockFlags.updateLockFile) throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef); - auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"; - - auto path = *sourcePath + "/" + relPath; - - bool lockFileExists = pathExists(path); + bool lockFileExists = pathExists(*outputLockFilePath); if (lockFileExists) { auto s = chomp(diff); if (s.empty()) - warn("updating lock file '%s'", path); + warn("updating lock file '%s'", *outputLockFilePath); else - warn("updating lock file '%s':\n%s", path, s); + warn("updating lock file '%s':\n%s", *outputLockFilePath, s); } else - warn("creating lock file '%s'", path); + warn("creating lock file '%s'", *outputLockFilePath); - newLockFile.write(path); + newLockFile.write(*outputLockFilePath); std::optional commitMessage = std::nullopt; if (lockFlags.commitLockFile) { + if (lockFlags.outputLockFilePath) { + throw Error("--commit-lock-file and --output-lock-file are currently incompatible"); + } std::string cm; cm = fetchSettings.commitLockFileSummary.get(); diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 10301d8aa..b5db56312 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -117,6 +117,12 @@ struct LockFlags /* Whether to commit changes to flake.lock. */ bool commitLockFile = false; + /* The path to a lock file to read instead of the `flake.lock` file in the top-level flake */ + std::optional referenceLockFilePath = std::nullopt; + + /* The path to a lock file to write to instead of the `flake.lock` file in the top-level flake */ + std::optional outputLockFilePath = std::nullopt; + /* Flake inputs to be overridden. */ std::map inputOverrides; -- cgit v1.2.3 From 3a1de4c3fe176256514455e1ca951bf28f53bd71 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Tue, 14 Mar 2023 12:02:03 +0100 Subject: Apply review suggestions Co-authored-by: Eelco Dolstra --- src/libexpr/flake/flake.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr/flake') diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index b5db56312..e7e201395 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -118,10 +118,10 @@ struct LockFlags bool commitLockFile = false; /* The path to a lock file to read instead of the `flake.lock` file in the top-level flake */ - std::optional referenceLockFilePath = std::nullopt; + std::optional referenceLockFilePath; /* The path to a lock file to write to instead of the `flake.lock` file in the top-level flake */ - std::optional outputLockFilePath = std::nullopt; + std::optional outputLockFilePath; /* Flake inputs to be overridden. */ std::map inputOverrides; -- cgit v1.2.3 From f1c9d83697e074c32f4efdcb2845bc25edc48f13 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Sun, 19 Mar 2023 14:12:49 +0100 Subject: Only allow reference lock files when allow-dirty is set --- src/libexpr/flake/flake.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/libexpr/flake') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 64ec4bdd2..0d55ce23e 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -334,6 +334,9 @@ LockedFlake lockFlake( } try { + if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { + throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false"); + } // FIXME: symlink attack auto oldLockFile = LockFile::read( -- cgit v1.2.3