2019-02-12 19:23:11 +02:00
|
|
|
#include "flake.hh"
|
2021-07-02 15:36:14 +03:00
|
|
|
#include "eval.hh"
|
2023-07-31 16:19:19 +03:00
|
|
|
#include "eval-settings.hh"
|
2019-06-04 21:01:21 +03:00
|
|
|
#include "lockfile.hh"
|
2018-11-29 20:18:36 +02:00
|
|
|
#include "primops.hh"
|
|
|
|
#include "eval-inline.hh"
|
2020-01-21 17:27:53 +02:00
|
|
|
#include "store-api.hh"
|
2020-03-30 15:03:28 +03:00
|
|
|
#include "fetchers.hh"
|
2020-02-02 14:14:34 +02:00
|
|
|
#include "finally.hh"
|
2022-03-01 03:29:34 +02:00
|
|
|
#include "fetch-settings.hh"
|
2018-11-29 20:18:36 +02:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2019-05-29 16:31:07 +03:00
|
|
|
using namespace flake;
|
|
|
|
|
|
|
|
namespace flake {
|
|
|
|
|
2020-10-06 12:16:32 +03:00
|
|
|
typedef std::pair<fetchers::Tree, FlakeRef> FetchedFlake;
|
2020-06-01 10:39:15 +03:00
|
|
|
typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache;
|
2019-09-19 00:59:45 +03:00
|
|
|
|
2020-06-01 10:39:15 +03:00
|
|
|
static std::optional<FetchedFlake> lookupInFlakeCache(
|
2020-01-29 22:10:27 +02:00
|
|
|
const FlakeCache & flakeCache,
|
2019-09-19 00:59:45 +03:00
|
|
|
const FlakeRef & flakeRef)
|
|
|
|
{
|
|
|
|
// FIXME: inefficient.
|
2020-01-29 22:10:27 +02:00
|
|
|
for (auto & i : flakeCache) {
|
|
|
|
if (flakeRef == i.first) {
|
2019-09-19 00:59:45 +03:00
|
|
|
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
2020-06-01 10:39:15 +03:00
|
|
|
flakeRef, i.first, i.second.second);
|
2019-09-19 00:59:45 +03:00
|
|
|
return i.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 10:39:15 +03:00
|
|
|
return std::nullopt;
|
2019-09-19 00:59:45 +03:00
|
|
|
}
|
2019-06-21 20:04:58 +03:00
|
|
|
|
2020-04-06 15:56:13 +03:00
|
|
|
static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
2020-02-02 13:41:23 +02:00
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & originalRef,
|
|
|
|
bool allowLookup,
|
|
|
|
FlakeCache & flakeCache)
|
|
|
|
{
|
2020-06-01 10:39:15 +03:00
|
|
|
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
|
|
|
|
FlakeRef resolvedRef = originalRef;
|
2020-02-02 13:41:23 +02:00
|
|
|
|
2020-06-01 10:39:15 +03:00
|
|
|
if (!fetched) {
|
|
|
|
if (originalRef.input.isDirect()) {
|
|
|
|
fetched.emplace(originalRef.fetchTree(state.store));
|
|
|
|
} else {
|
|
|
|
if (allowLookup) {
|
|
|
|
resolvedRef = originalRef.resolve(state.store);
|
|
|
|
auto fetchedResolved = lookupInFlakeCache(flakeCache, originalRef);
|
|
|
|
if (!fetchedResolved) fetchedResolved.emplace(resolvedRef.fetchTree(state.store));
|
2020-09-25 02:05:47 +03:00
|
|
|
flakeCache.push_back({resolvedRef, *fetchedResolved});
|
|
|
|
fetched.emplace(*fetchedResolved);
|
2020-06-01 10:39:15 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef);
|
|
|
|
}
|
|
|
|
}
|
2020-09-25 02:05:47 +03:00
|
|
|
flakeCache.push_back({originalRef, *fetched});
|
2020-06-01 10:39:15 +03:00
|
|
|
}
|
2020-06-09 14:45:07 +03:00
|
|
|
|
2020-09-25 02:05:47 +03:00
|
|
|
auto [tree, lockedRef] = *fetched;
|
2020-02-02 13:41:23 +02:00
|
|
|
|
|
|
|
debug("got tree '%s' from '%s'",
|
|
|
|
state.store->printStorePath(tree.storePath), lockedRef);
|
|
|
|
|
2021-10-07 15:07:51 +03:00
|
|
|
state.allowPath(tree.storePath);
|
2020-02-02 13:41:23 +02:00
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
2020-02-02 13:41:23 +02:00
|
|
|
|
2020-04-06 15:56:13 +03:00
|
|
|
return {std::move(tree), resolvedRef, lockedRef};
|
2020-02-02 13:41:23 +02:00
|
|
|
}
|
|
|
|
|
2022-03-04 20:31:59 +02:00
|
|
|
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
2019-09-09 17:34:44 +03:00
|
|
|
{
|
2020-12-12 03:15:11 +02:00
|
|
|
if (value.isThunk() && value.isTrivial())
|
2019-09-09 17:34:44 +03:00
|
|
|
state.forceValue(value, pos);
|
2020-10-26 21:45:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-17 15:42:52 +02:00
|
|
|
static void expectType(EvalState & state, ValueType type,
|
2022-03-04 20:31:59 +02:00
|
|
|
Value & value, const PosIdx pos)
|
2020-10-26 21:45:39 +02:00
|
|
|
{
|
|
|
|
forceTrivialValue(state, value, pos);
|
2020-12-17 15:45:45 +02:00
|
|
|
if (value.type() != type)
|
2019-09-09 17:34:44 +03:00
|
|
|
throw Error("expected %s but got %s at %s",
|
2022-03-04 20:31:59 +02:00
|
|
|
showType(type), showType(value.type()), state.positions[pos]);
|
2019-09-09 17:34:44 +03:00
|
|
|
}
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
2022-03-04 20:31:59 +02:00
|
|
|
EvalState & state, Value * value, const PosIdx pos,
|
2022-02-02 20:41:45 +02:00
|
|
|
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
|
|
|
static FlakeInput parseFlakeInput(EvalState & state,
|
2022-03-04 20:31:59 +02:00
|
|
|
const std::string & inputName, Value * value, const PosIdx pos,
|
2022-02-02 20:41:45 +02:00
|
|
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
{
|
2020-12-12 00:32:45 +02:00
|
|
|
expectType(state, nAttrs, *value, pos);
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2020-06-11 15:40:21 +03:00
|
|
|
FlakeInput input;
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
|
|
|
auto sInputs = state.symbols.create("inputs");
|
|
|
|
auto sUrl = state.symbols.create("url");
|
|
|
|
auto sFlake = state.symbols.create("flake");
|
|
|
|
auto sFollows = state.symbols.create("follows");
|
|
|
|
|
2020-03-17 21:54:36 +02:00
|
|
|
fetchers::Attrs attrs;
|
2020-01-31 21:50:46 +02:00
|
|
|
std::optional<std::string> url;
|
|
|
|
|
2020-03-17 21:54:36 +02:00
|
|
|
for (nix::Attr attr : *(value->attrs)) {
|
2020-01-31 21:50:46 +02:00
|
|
|
try {
|
2020-05-06 18:48:18 +03:00
|
|
|
if (attr.name == sUrl) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nString, *attr.value, attr.pos);
|
2020-01-31 21:50:46 +02:00
|
|
|
url = attr.value->string.s;
|
|
|
|
attrs.emplace("url", *url);
|
|
|
|
} else if (attr.name == sFlake) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nBool, *attr.value, attr.pos);
|
2020-01-31 21:50:46 +02:00
|
|
|
input.isFlake = attr.value->boolean;
|
|
|
|
} else if (attr.name == sInputs) {
|
2022-03-04 20:31:59 +02:00
|
|
|
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
|
2020-01-31 21:50:46 +02:00
|
|
|
} else if (attr.name == sFollows) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nString, *attr.value, attr.pos);
|
2022-02-02 20:41:45 +02:00
|
|
|
auto follows(parseInputPath(attr.value->string.s));
|
|
|
|
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
|
|
|
input.follows = follows;
|
2020-01-31 21:50:46 +02:00
|
|
|
} else {
|
2023-04-03 19:15:12 +03:00
|
|
|
// Allow selecting a subset of enum values
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
2021-01-08 05:13:42 +02:00
|
|
|
switch (attr.value->type()) {
|
|
|
|
case nString:
|
2022-03-05 15:40:24 +02:00
|
|
|
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
2021-01-08 05:13:42 +02:00
|
|
|
break;
|
|
|
|
case nBool:
|
2022-03-05 15:40:24 +02:00
|
|
|
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
2021-01-08 05:13:42 +02:00
|
|
|
break;
|
|
|
|
case nInt:
|
2022-03-05 15:40:24 +02:00
|
|
|
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
|
2021-01-08 05:13:42 +02:00
|
|
|
break;
|
|
|
|
default:
|
2021-01-09 02:12:21 +02:00
|
|
|
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
2022-03-05 15:40:24 +02:00
|
|
|
state.symbols[attr.name], showType(*attr.value));
|
2021-01-08 03:53:57 +02:00
|
|
|
}
|
2023-04-03 19:15:12 +03:00
|
|
|
#pragma GCC diagnostic pop
|
2020-01-29 15:57:57 +02:00
|
|
|
}
|
2020-01-31 21:50:46 +02:00
|
|
|
} catch (Error & e) {
|
2022-03-05 15:40:24 +02:00
|
|
|
e.addTrace(
|
|
|
|
state.positions[attr.pos],
|
2022-11-28 17:39:28 +02:00
|
|
|
hintfmt("while evaluating flake attribute '%s'", state.symbols[attr.name]));
|
2020-01-31 21:50:46 +02:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs.count("type"))
|
|
|
|
try {
|
|
|
|
input.ref = FlakeRef::fromAttrs(attrs);
|
|
|
|
} catch (Error & e) {
|
2022-11-28 17:39:28 +02:00
|
|
|
e.addTrace(state.positions[pos], hintfmt("while evaluating flake input"));
|
2020-01-31 21:50:46 +02:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attrs.erase("url");
|
|
|
|
if (!attrs.empty())
|
2022-03-04 20:31:59 +02:00
|
|
|
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
2020-01-31 21:50:46 +02:00
|
|
|
if (url)
|
2021-12-13 21:54:43 +02:00
|
|
|
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
}
|
|
|
|
|
2020-06-11 15:40:21 +03:00
|
|
|
if (!input.follows && !input.ref)
|
|
|
|
input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}});
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
2022-03-04 20:31:59 +02:00
|
|
|
EvalState & state, Value * value, const PosIdx pos,
|
2022-02-02 20:41:45 +02:00
|
|
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
{
|
|
|
|
std::map<FlakeId, FlakeInput> inputs;
|
|
|
|
|
2020-12-12 00:32:45 +02:00
|
|
|
expectType(state, nAttrs, *value, pos);
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2020-03-17 21:54:36 +02:00
|
|
|
for (nix::Attr & inputAttr : *(*value).attrs) {
|
2022-03-05 15:40:24 +02:00
|
|
|
inputs.emplace(state.symbols[inputAttr.name],
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
parseFlakeInput(state,
|
2022-03-05 15:40:24 +02:00
|
|
|
state.symbols[inputAttr.name],
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
inputAttr.value,
|
2022-03-04 20:31:59 +02:00
|
|
|
inputAttr.pos,
|
2022-02-02 20:41:45 +02:00
|
|
|
baseDir,
|
|
|
|
lockRootPath));
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return inputs;
|
|
|
|
}
|
|
|
|
|
2020-02-02 13:41:23 +02:00
|
|
|
static Flake getFlake(
|
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & originalRef,
|
|
|
|
bool allowLookup,
|
2022-02-02 20:41:45 +02:00
|
|
|
FlakeCache & flakeCache,
|
|
|
|
InputPath lockRootPath)
|
2018-11-30 17:11:15 +02:00
|
|
|
{
|
2020-04-06 15:56:13 +03:00
|
|
|
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
state, originalRef, allowLookup, flakeCache);
|
2019-02-12 21:35:03 +02:00
|
|
|
|
2019-05-01 21:38:41 +03:00
|
|
|
// Guard against symlink attacks.
|
2021-12-13 21:54:43 +02:00
|
|
|
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
|
|
|
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
2020-01-21 17:27:53 +02:00
|
|
|
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
|
|
|
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
2020-02-02 01:05:53 +02:00
|
|
|
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
2020-01-21 17:27:53 +02:00
|
|
|
|
|
|
|
Flake flake {
|
|
|
|
.originalRef = originalRef,
|
2020-04-06 15:56:13 +03:00
|
|
|
.resolvedRef = resolvedRef,
|
2020-02-02 01:05:53 +02:00
|
|
|
.lockedRef = lockedRef,
|
2020-01-21 17:27:53 +02:00
|
|
|
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
|
|
|
};
|
2019-05-01 12:38:48 +03:00
|
|
|
|
2020-01-21 17:27:53 +02:00
|
|
|
if (!pathExists(flakeFile))
|
2020-02-02 01:05:53 +02:00
|
|
|
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
|
2019-04-19 12:43:56 +03:00
|
|
|
|
2018-11-29 20:18:36 +02:00
|
|
|
Value vInfo;
|
2023-04-06 14:15:50 +03:00
|
|
|
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2023-04-06 16:25:06 +03:00
|
|
|
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2019-09-09 17:34:44 +03:00
|
|
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nString, *description->value, description->pos);
|
2020-02-14 23:45:33 +02:00
|
|
|
flake.description = description->value->string.s;
|
2019-09-09 17:34:44 +03:00
|
|
|
}
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2019-05-30 00:09:23 +03:00
|
|
|
auto sInputs = state.symbols.create("inputs");
|
2019-05-29 16:12:22 +03:00
|
|
|
|
2020-02-14 23:45:33 +02:00
|
|
|
if (auto inputs = vInfo.attrs->get(sInputs))
|
2022-03-04 20:31:59 +02:00
|
|
|
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
|
2019-03-21 10:30:16 +02:00
|
|
|
|
2019-05-30 00:09:23 +03:00
|
|
|
auto sOutputs = state.symbols.create("outputs");
|
2019-05-29 16:12:22 +03:00
|
|
|
|
2019-05-30 00:09:23 +03:00
|
|
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nFunction, *outputs->value, outputs->pos);
|
2019-08-30 12:22:34 +03:00
|
|
|
|
2021-10-06 18:08:08 +03:00
|
|
|
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
2020-09-23 15:08:23 +03:00
|
|
|
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
2019-08-30 17:27:51 +03:00
|
|
|
if (formal.name != state.sSelf)
|
2022-03-05 15:40:24 +02:00
|
|
|
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
|
|
|
.ref = parseFlakeRef(state.symbols[formal.name])
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
});
|
2019-08-30 12:22:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-29 20:18:36 +02:00
|
|
|
} else
|
2020-02-02 13:41:23 +02:00
|
|
|
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2020-10-26 21:45:39 +02:00
|
|
|
auto sNixConfig = state.symbols.create("nixConfig");
|
|
|
|
|
|
|
|
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
2022-03-04 20:31:59 +02:00
|
|
|
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
2020-10-26 21:45:39 +02:00
|
|
|
|
2020-11-26 13:34:43 +02:00
|
|
|
for (auto & setting : *nixConfig->value->attrs) {
|
2022-03-04 20:31:59 +02:00
|
|
|
forceTrivialValue(state, *setting.value, setting.pos);
|
2020-12-17 15:45:45 +02:00
|
|
|
if (setting.value->type() == nString)
|
2022-03-05 15:40:24 +02:00
|
|
|
flake.config.settings.emplace(
|
|
|
|
state.symbols[setting.name],
|
2023-01-19 14:23:04 +02:00
|
|
|
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
|
2021-11-12 17:28:39 +02:00
|
|
|
else if (setting.value->type() == nPath) {
|
Use `std::set<StringContextElem>` not `PathSet` for string contexts
Motivation
`PathSet` is not correct because string contexts have other forms
(`Built` and `DrvDeep`) that are not rendered as plain store paths.
Instead of wrongly using `PathSet`, or "stringly typed" using
`StringSet`, use `std::std<StringContextElem>`.
-----
In support of this change, `NixStringContext` is now defined as
`std::std<StringContextElem>` not `std:vector<StringContextElem>`. The
old definition was just used by a `getContext` method which was only
used by the eval cache. It can be deleted altogether since the types are
now unified and the preexisting `copyContext` function already suffices.
Summarizing the previous paragraph:
Old:
- `value/context.hh`: `NixStringContext = std::vector<StringContextElem>`
- `value.hh`: `NixStringContext Value::getContext(...)`
- `value.hh`: `copyContext(...)`
New:
- `value/context.hh`: `NixStringContext = std::set<StringContextElem>`
- `value.hh`: `copyContext(...)`
----
The string representation of string context elements no longer contains
the store dir. The diff of `src/libexpr/tests/value/context.cc` should
make clear what the new representation is, so we recommend reviewing
that file first. This was done for two reasons:
Less API churn:
`Value::mkString` and friends did not take a `Store` before. But if
`NixStringContextElem::{parse, to_string}` *do* take a store (as they
did before), then we cannot have the `Value` functions use them (in
order to work with the fully-structured `NixStringContext`) without
adding that argument.
That would have been a lot of churn of threading the store, and this
diff is already large enough, so the easier and less invasive thing to
do was simply make the element `parse` and `to_string` functions not
take the `Store` reference, and the easiest way to do that was to simply
drop the store dir.
Space usage:
Dropping the `/nix/store/` (or similar) from the internal representation
will safe space in the heap of the Nix programming being interpreted. If
the heap contains many strings with non-trivial contexts, the saving
could add up to something significant.
----
The eval cache version is bumped.
The eval cache serialization uses `NixStringContextElem::{parse,
to_string}`, and since those functions are changed per the above, that
means the on-disk representation is also changed.
This is simply done by changing the name of the used for the eval cache
from `eval-cache-v4` to eval-cache-v5`.
----
To avoid some duplication `EvalCache::mkPathString` is added to abstract
over the simple case of turning a store path to a string with just that
string in the context.
Context
This PR picks up where #7543 left off. That one introduced the fully
structured `NixStringContextElem` data type, but kept `PathSet context`
as an awkward middle ground between internal `char[][]` interpreter heap
string contexts and `NixStringContext` fully parsed string contexts.
The infelicity of `PathSet context` was specifically called out during
Nix team group review, but it was agreeing that fixing it could be left
as future work. This is that future work.
A possible follow-up step would be to get rid of the `char[][]`
evaluator heap representation, too, but it is not yet clear how to do
that. To use `NixStringContextElem` there we would need to get the STL
containers to GC pointers in the GC build, and I am not sure how to do
that.
----
PR #7543 effectively is writing the inverse of a `mkPathString`,
`mkOutputString`, and one more such function for the `DrvDeep` case. I
would like that PR to have property tests ensuring it is actually the
inverse as expected.
This PR sets things up nicely so that reworking that PR to be in that
more elegant and better tested way is possible.
Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>
2023-01-29 03:31:10 +02:00
|
|
|
NixStringContext emptyContext = {};
|
2022-01-21 17:20:54 +02:00
|
|
|
flake.config.settings.emplace(
|
2022-03-05 15:40:24 +02:00
|
|
|
state.symbols[setting.name],
|
2022-11-29 01:25:36 +02:00
|
|
|
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());
|
2021-11-12 17:28:39 +02:00
|
|
|
}
|
2020-12-17 15:45:45 +02:00
|
|
|
else if (setting.value->type() == nInt)
|
2022-03-05 15:40:24 +02:00
|
|
|
flake.config.settings.emplace(
|
|
|
|
state.symbols[setting.name],
|
2023-01-19 14:23:04 +02:00
|
|
|
state.forceInt(*setting.value, setting.pos, ""));
|
2020-12-17 15:45:45 +02:00
|
|
|
else if (setting.value->type() == nBool)
|
2022-03-05 15:40:24 +02:00
|
|
|
flake.config.settings.emplace(
|
|
|
|
state.symbols[setting.name],
|
2023-01-19 14:23:04 +02:00
|
|
|
Explicit<bool> { state.forceBool(*setting.value, setting.pos, "") });
|
2020-12-17 15:45:45 +02:00
|
|
|
else if (setting.value->type() == nList) {
|
2020-10-26 21:45:39 +02:00
|
|
|
std::vector<std::string> ss;
|
2021-11-24 21:21:34 +02:00
|
|
|
for (auto elem : setting.value->listItems()) {
|
2020-12-17 15:45:45 +02:00
|
|
|
if (elem->type() != nString)
|
2020-11-26 13:34:43 +02:00
|
|
|
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
2022-03-05 15:40:24 +02:00
|
|
|
state.symbols[setting.name], showType(*setting.value));
|
2023-01-19 14:23:04 +02:00
|
|
|
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
|
2020-10-26 21:45:39 +02:00
|
|
|
}
|
2022-03-05 15:40:24 +02:00
|
|
|
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
2020-10-26 21:45:39 +02:00
|
|
|
}
|
|
|
|
else
|
2020-11-26 13:34:43 +02:00
|
|
|
throw TypeError("flake configuration setting '%s' is %s",
|
2022-03-05 15:40:24 +02:00
|
|
|
state.symbols[setting.name], showType(*setting.value));
|
2020-10-26 21:45:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-29 16:12:22 +03:00
|
|
|
for (auto & attr : *vInfo.attrs) {
|
2020-10-26 18:59:36 +02:00
|
|
|
if (attr.name != state.sDescription &&
|
2019-05-30 00:09:23 +03:00
|
|
|
attr.name != sInputs &&
|
2020-10-26 21:45:39 +02:00
|
|
|
attr.name != sOutputs &&
|
|
|
|
attr.name != sNixConfig)
|
2019-05-29 16:12:22 +03:00
|
|
|
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
2022-03-05 15:40:24 +02:00
|
|
|
lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
|
2019-05-29 16:12:22 +03:00
|
|
|
}
|
|
|
|
|
2018-11-29 20:18:36 +02:00
|
|
|
return flake;
|
|
|
|
}
|
|
|
|
|
2022-02-02 20:41:45 +02:00
|
|
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
|
|
|
{
|
|
|
|
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
|
|
|
}
|
|
|
|
|
2019-09-19 00:59:45 +03:00
|
|
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
|
|
|
{
|
2020-01-29 22:10:27 +02:00
|
|
|
FlakeCache flakeCache;
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
return getFlake(state, originalRef, allowLookup, flakeCache);
|
2019-03-21 10:30:16 +02:00
|
|
|
}
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
/* Compute an in-memory lock file for the specified top-level flake,
|
2020-09-27 23:04:06 +03:00
|
|
|
and optionally write it to file, if the flake is writable. */
|
2020-01-22 21:59:59 +02:00
|
|
|
LockedFlake lockFlake(
|
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & topRef,
|
2020-01-29 15:57:57 +02:00
|
|
|
const LockFlags & lockFlags)
|
2019-05-01 12:38:48 +03:00
|
|
|
{
|
2023-03-17 16:33:48 +02:00
|
|
|
experimentalFeatureSettings.require(Xp::Flakes);
|
2019-10-16 18:45:09 +03:00
|
|
|
|
2020-01-29 22:10:27 +02:00
|
|
|
FlakeCache flakeCache;
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2022-03-01 03:29:34 +02:00
|
|
|
auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
|
2021-07-02 15:36:14 +03:00
|
|
|
|
|
|
|
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
|
2019-06-04 20:10:35 +03:00
|
|
|
|
2021-07-01 17:54:22 +03:00
|
|
|
if (lockFlags.applyNixConfig) {
|
|
|
|
flake.config.apply();
|
2021-11-05 17:17:49 +02:00
|
|
|
state.store->setOptions();
|
2021-07-01 17:54:22 +03:00
|
|
|
}
|
2021-07-01 00:23:47 +03:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
try {
|
2023-03-19 15:12:49 +02:00
|
|
|
if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) {
|
|
|
|
throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false");
|
|
|
|
}
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
// FIXME: symlink attack
|
|
|
|
auto oldLockFile = LockFile::read(
|
2023-03-13 22:08:52 +02:00
|
|
|
lockFlags.referenceLockFilePath.value_or(
|
|
|
|
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"));
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
debug("old lock file: %s", oldLockFile);
|
|
|
|
|
|
|
|
std::map<InputPath, FlakeInput> overrides;
|
|
|
|
std::set<InputPath> overridesUsed, updatesUsed;
|
|
|
|
|
|
|
|
for (auto & i : lockFlags.inputOverrides)
|
|
|
|
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
|
|
|
|
|
|
|
|
LockFile newLockFile;
|
|
|
|
|
|
|
|
std::vector<FlakeRef> parents;
|
|
|
|
|
|
|
|
std::function<void(
|
|
|
|
const FlakeInputs & flakeInputs,
|
2022-12-07 13:58:58 +02:00
|
|
|
ref<Node> node,
|
2021-01-27 15:02:54 +02:00
|
|
|
const InputPath & inputPathPrefix,
|
2021-07-12 04:07:53 +03:00
|
|
|
std::shared_ptr<const Node> oldNode,
|
2022-02-02 20:41:45 +02:00
|
|
|
const InputPath & lockRootPath,
|
2021-12-28 14:30:06 +02:00
|
|
|
const Path & parentPath,
|
|
|
|
bool trustLock)>
|
2021-01-27 15:02:54 +02:00
|
|
|
computeLocks;
|
|
|
|
|
|
|
|
computeLocks = [&](
|
2022-12-07 13:58:58 +02:00
|
|
|
/* The inputs of this node, either from flake.nix or
|
|
|
|
flake.lock. */
|
2021-01-27 15:02:54 +02:00
|
|
|
const FlakeInputs & flakeInputs,
|
2022-12-07 13:58:58 +02:00
|
|
|
/* The node whose locks are to be updated.*/
|
|
|
|
ref<Node> node,
|
|
|
|
/* The path to this node in the lock file graph. */
|
2021-01-27 15:02:54 +02:00
|
|
|
const InputPath & inputPathPrefix,
|
2022-12-07 13:58:58 +02:00
|
|
|
/* The old node, if any, from which locks can be
|
|
|
|
copied. */
|
2021-07-12 04:07:53 +03:00
|
|
|
std::shared_ptr<const Node> oldNode,
|
2022-02-02 20:41:45 +02:00
|
|
|
const InputPath & lockRootPath,
|
2021-12-28 14:30:06 +02:00
|
|
|
const Path & parentPath,
|
|
|
|
bool trustLock)
|
2021-01-27 15:02:54 +02:00
|
|
|
{
|
|
|
|
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
|
|
|
|
|
|
|
/* Get the overrides (i.e. attributes of the form
|
|
|
|
'inputs.nixops.inputs.nixpkgs.url = ...'). */
|
2021-07-12 04:07:53 +03:00
|
|
|
for (auto & [id, input] : flakeInputs) {
|
2021-01-27 15:02:54 +02:00
|
|
|
for (auto & [idOverride, inputOverride] : input.overrides) {
|
|
|
|
auto inputPath(inputPathPrefix);
|
|
|
|
inputPath.push_back(id);
|
|
|
|
inputPath.push_back(idOverride);
|
|
|
|
overrides.insert_or_assign(inputPath, inputOverride);
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
}
|
2020-03-27 22:08:41 +02:00
|
|
|
}
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2022-07-13 14:39:06 +03:00
|
|
|
/* Check whether this input has overrides for a
|
|
|
|
non-existent input. */
|
|
|
|
for (auto [inputPath, inputOverride] : overrides) {
|
|
|
|
auto inputPath2(inputPath);
|
|
|
|
auto follow = inputPath2.back();
|
|
|
|
inputPath2.pop_back();
|
|
|
|
if (inputPath2 == inputPathPrefix && !flakeInputs.count(follow))
|
|
|
|
warn(
|
|
|
|
"input '%s' has an override for a non-existent input '%s'",
|
|
|
|
printInputPath(inputPathPrefix), follow);
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
/* Go over the flake inputs, resolve/fetch them if
|
|
|
|
necessary (i.e. if they're new or the flakeref changed
|
|
|
|
from what's in the lock file). */
|
|
|
|
for (auto & [id, input2] : flakeInputs) {
|
|
|
|
auto inputPath(inputPathPrefix);
|
|
|
|
inputPath.push_back(id);
|
|
|
|
auto inputPathS = printInputPath(inputPath);
|
|
|
|
debug("computing input '%s'", inputPathS);
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
/* Do we have an override for this input from one of the
|
|
|
|
ancestors? */
|
|
|
|
auto i = overrides.find(inputPath);
|
|
|
|
bool hasOverride = i != overrides.end();
|
2021-06-29 14:53:56 +03:00
|
|
|
if (hasOverride) {
|
|
|
|
overridesUsed.insert(inputPath);
|
|
|
|
// Respect the “flakeness” of the input even if we
|
|
|
|
// override it
|
|
|
|
i->second.isFlake = input2.isFlake;
|
|
|
|
}
|
2021-01-27 15:02:54 +02:00
|
|
|
auto & input = hasOverride ? i->second : input2;
|
|
|
|
|
|
|
|
/* Resolve 'follows' later (since it may refer to an input
|
|
|
|
path we haven't processed yet. */
|
|
|
|
if (input.follows) {
|
|
|
|
InputPath target;
|
2021-07-12 04:07:53 +03:00
|
|
|
|
2022-02-02 20:41:45 +02:00
|
|
|
target.insert(target.end(), input.follows->begin(), input.follows->end());
|
2021-07-12 04:07:53 +03:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
|
|
|
|
node->inputs.insert_or_assign(id, target);
|
|
|
|
continue;
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
}
|
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
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? */
|
|
|
|
std::shared_ptr<LockedNode> oldLock;
|
|
|
|
|
|
|
|
updatesUsed.insert(inputPath);
|
|
|
|
|
|
|
|
if (oldNode && !lockFlags.inputUpdates.count(inputPath))
|
|
|
|
if (auto oldLock2 = get(oldNode->inputs, id))
|
|
|
|
if (auto oldLock3 = std::get_if<0>(&*oldLock2))
|
|
|
|
oldLock = *oldLock3;
|
|
|
|
|
|
|
|
if (oldLock
|
|
|
|
&& oldLock->originalRef == *input.ref
|
|
|
|
&& !hasOverride)
|
|
|
|
{
|
|
|
|
debug("keeping existing input '%s'", inputPathS);
|
|
|
|
|
|
|
|
/* Copy the input from the old lock since its flakeref
|
|
|
|
didn't change and there is no override from a
|
|
|
|
higher level flake. */
|
2022-12-07 13:58:58 +02:00
|
|
|
auto childNode = make_ref<LockedNode>(
|
2021-01-27 15:02:54 +02:00
|
|
|
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake);
|
|
|
|
|
|
|
|
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. */
|
|
|
|
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
|
|
|
|
|
2021-11-15 19:44:27 +02:00
|
|
|
auto mustRefetch =
|
2021-01-27 15:02:54 +02:00
|
|
|
lb != lockFlags.inputUpdates.end()
|
|
|
|
&& lb->size() > inputPath.size()
|
|
|
|
&& std::equal(inputPath.begin(), inputPath.end(), lb->begin());
|
|
|
|
|
2021-11-15 19:44:27 +02:00
|
|
|
FlakeInputs fakeInputs;
|
|
|
|
|
|
|
|
if (!mustRefetch) {
|
2021-01-27 15:02:54 +02:00
|
|
|
/* 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. */
|
|
|
|
for (auto & i : oldLock->inputs) {
|
|
|
|
if (auto lockedNode = std::get_if<0>(&i.second)) {
|
|
|
|
fakeInputs.emplace(i.first, FlakeInput {
|
|
|
|
.ref = (*lockedNode)->originalRef,
|
|
|
|
.isFlake = (*lockedNode)->isFlake,
|
|
|
|
});
|
|
|
|
} else if (auto follows = std::get_if<1>(&i.second)) {
|
2022-12-07 13:58:58 +02:00
|
|
|
if (!trustLock) {
|
2021-12-28 14:30:06 +02:00
|
|
|
// It is possible that the flake has changed,
|
2022-09-06 20:20:31 +03:00
|
|
|
// so we must confirm all the follows that are in the lock file are also in the flake.
|
2021-12-28 14:30:06 +02:00
|
|
|
auto overridePath(inputPath);
|
|
|
|
overridePath.push_back(i.first);
|
|
|
|
auto o = overrides.find(overridePath);
|
|
|
|
// If the override disappeared, we have to refetch the flake,
|
2022-09-06 20:20:31 +03:00
|
|
|
// since some of the inputs may not be present in the lock file.
|
2021-12-28 14:30:06 +02:00
|
|
|
if (o == overrides.end()) {
|
|
|
|
mustRefetch = true;
|
|
|
|
// There's no point populating the rest of the fake inputs,
|
|
|
|
// since we'll refetch the flake anyways.
|
|
|
|
break;
|
|
|
|
}
|
2021-11-10 13:56:22 +02:00
|
|
|
}
|
2022-02-02 20:41:45 +02:00
|
|
|
auto absoluteFollows(lockRootPath);
|
|
|
|
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
2021-01-27 15:02:54 +02:00
|
|
|
fakeInputs.emplace(i.first, FlakeInput {
|
2022-02-02 20:41:45 +02:00
|
|
|
.follows = absoluteFollows,
|
2021-01-27 15:02:54 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2021-11-15 19:44:27 +02:00
|
|
|
computeLocks(
|
|
|
|
mustRefetch
|
2022-02-02 20:41:45 +02:00
|
|
|
? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
|
2021-11-15 19:44:27 +02:00
|
|
|
: fakeInputs,
|
2022-02-02 20:41:45 +02:00
|
|
|
childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
|
2021-11-15 19:44:27 +02:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
} else {
|
|
|
|
/* We need to create a new lock file entry. So fetch
|
|
|
|
this input. */
|
|
|
|
debug("creating new input '%s'", inputPathS);
|
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
if (!lockFlags.allowUnlocked && !input.ref->input.isLocked())
|
|
|
|
throw Error("cannot update unlocked flake input '%s' in pure mode", inputPathS);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
2022-06-13 19:49:16 +03:00
|
|
|
/* Note: in case of an --override-input, we use
|
|
|
|
the *original* ref (input2.ref) for the
|
|
|
|
"original" field, rather than the
|
|
|
|
override. This ensures that the override isn't
|
|
|
|
nuked the next time we update the lock
|
|
|
|
file. That is, overrides are sticky unless you
|
|
|
|
use --no-write-lock-file. */
|
|
|
|
auto ref = input2.ref ? *input2.ref : *input.ref;
|
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
if (input.isFlake) {
|
2021-07-12 04:07:53 +03:00
|
|
|
Path localPath = parentPath;
|
|
|
|
FlakeRef localRef = *input.ref;
|
|
|
|
|
|
|
|
// If this input is a path, recurse it down.
|
|
|
|
// This allows us to resolve path inputs relative to the current flake.
|
2021-09-21 15:07:16 +03:00
|
|
|
if (localRef.input.getType() == "path")
|
|
|
|
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
2021-07-12 04:07:53 +03:00
|
|
|
|
2022-02-02 20:41:45 +02:00
|
|
|
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
auto childNode = make_ref<LockedNode>(inputFlake.lockedRef, ref);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
node->inputs.insert_or_assign(id, childNode);
|
|
|
|
|
|
|
|
/* Guard against circular flake imports. */
|
|
|
|
for (auto & parent : parents)
|
|
|
|
if (parent == *input.ref)
|
|
|
|
throw Error("found circular import of flake '%s'", parent);
|
|
|
|
parents.push_back(*input.ref);
|
|
|
|
Finally cleanup([&]() { parents.pop_back(); });
|
|
|
|
|
|
|
|
/* Recursively process the inputs of this
|
|
|
|
flake. Also, unless we already have this flake
|
|
|
|
in the top-level lock file, use this flake's
|
|
|
|
own lock file. */
|
|
|
|
computeLocks(
|
|
|
|
inputFlake.inputs, childNode, inputPath,
|
|
|
|
oldLock
|
|
|
|
? std::dynamic_pointer_cast<const Node>(oldLock)
|
|
|
|
: LockFile::read(
|
2022-12-12 13:46:13 +02:00
|
|
|
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(),
|
2022-12-07 13:58:58 +02:00
|
|
|
oldLock ? lockRootPath : inputPath,
|
|
|
|
localPath,
|
|
|
|
false);
|
2021-01-27 15:02:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
2021-07-02 15:36:14 +03:00
|
|
|
state, *input.ref, useRegistries, flakeCache);
|
2022-12-07 13:58:58 +02:00
|
|
|
|
|
|
|
auto childNode = make_ref<LockedNode>(lockedRef, ref, false);
|
|
|
|
|
|
|
|
node->inputs.insert_or_assign(id, childNode);
|
2021-01-27 15:02:54 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-27 22:08:41 +02:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
} catch (Error & e) {
|
|
|
|
e.addTrace({}, "while updating the flake input '%s'", inputPathS);
|
|
|
|
throw;
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
}
|
|
|
|
}
|
2021-01-27 15:02:54 +02:00
|
|
|
};
|
|
|
|
|
2021-07-12 04:07:53 +03:00
|
|
|
// Bring in the current ref for relative path resolution if we have it
|
2021-12-13 21:54:43 +02:00
|
|
|
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
2021-07-12 04:07:53 +03:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
computeLocks(
|
2022-12-07 13:58:58 +02:00
|
|
|
flake.inputs,
|
|
|
|
newLockFile.root,
|
|
|
|
{},
|
2022-12-12 13:46:13 +02:00
|
|
|
lockFlags.recreateLockFile ? nullptr : oldLockFile.root.get_ptr(),
|
2022-12-07 13:58:58 +02:00
|
|
|
{},
|
|
|
|
parentPath,
|
|
|
|
false);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
for (auto & i : lockFlags.inputOverrides)
|
|
|
|
if (!overridesUsed.count(i.first))
|
|
|
|
warn("the flag '--override-input %s %s' does not match any input",
|
|
|
|
printInputPath(i.first), i.second);
|
|
|
|
|
|
|
|
for (auto & i : lockFlags.inputUpdates)
|
|
|
|
if (!updatesUsed.count(i))
|
|
|
|
warn("the flag '--update-input %s' does not match any input", printInputPath(i));
|
|
|
|
|
|
|
|
/* Check 'follows' inputs. */
|
|
|
|
newLockFile.check();
|
|
|
|
|
|
|
|
debug("new lock file: %s", newLockFile);
|
|
|
|
|
2023-03-13 22:08:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
/* Check whether we need to / can write the new lock file. */
|
2023-03-13 22:08:52 +02:00
|
|
|
if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) {
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
|
|
|
|
|
|
|
if (lockFlags.writeLockFile) {
|
2023-03-13 22:08:52 +02:00
|
|
|
if (outputLockFilePath) {
|
2022-12-07 13:58:58 +02:00
|
|
|
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
2022-03-01 03:29:34 +02:00
|
|
|
if (fetchSettings.warnDirty)
|
2022-12-07 13:58:58 +02:00
|
|
|
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
2021-01-27 15:02:54 +02:00
|
|
|
} else {
|
|
|
|
if (!lockFlags.updateLockFile)
|
|
|
|
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
|
|
|
|
2023-03-13 22:08:52 +02:00
|
|
|
bool lockFileExists = pathExists(*outputLockFilePath);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
if (lockFileExists) {
|
|
|
|
auto s = chomp(diff);
|
|
|
|
if (s.empty())
|
2023-03-13 22:08:52 +02:00
|
|
|
warn("updating lock file '%s'", *outputLockFilePath);
|
2021-01-27 15:02:54 +02:00
|
|
|
else
|
2023-03-13 22:08:52 +02:00
|
|
|
warn("updating lock file '%s':\n%s", *outputLockFilePath, s);
|
2021-01-27 15:02:54 +02:00
|
|
|
} else
|
2023-03-13 22:08:52 +02:00
|
|
|
warn("creating lock file '%s'", *outputLockFilePath);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
2023-03-13 22:08:52 +02:00
|
|
|
newLockFile.write(*outputLockFilePath);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
2022-01-13 22:01:04 +02:00
|
|
|
std::optional<std::string> commitMessage = std::nullopt;
|
|
|
|
if (lockFlags.commitLockFile) {
|
2023-03-13 22:08:52 +02:00
|
|
|
if (lockFlags.outputLockFilePath) {
|
|
|
|
throw Error("--commit-lock-file and --output-lock-file are currently incompatible");
|
|
|
|
}
|
2022-01-13 22:01:04 +02:00
|
|
|
std::string cm;
|
|
|
|
|
2022-03-01 03:29:34 +02:00
|
|
|
cm = fetchSettings.commitLockFileSummary.get();
|
2022-01-13 22:01:04 +02:00
|
|
|
|
|
|
|
if (cm == "") {
|
|
|
|
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
|
|
|
}
|
|
|
|
|
|
|
|
cm += "\n\nFlake lock file updates:\n\n";
|
|
|
|
cm += filterANSIEscapes(diff, true);
|
|
|
|
commitMessage = cm;
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
topRef.input.markChangedFile(
|
|
|
|
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
2022-01-13 22:01:04 +02:00
|
|
|
commitMessage);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
/* Rewriting the lockfile changed the top-level
|
|
|
|
repo, so we should re-read it. FIXME: we could
|
|
|
|
also just clear the 'rev' field... */
|
|
|
|
auto prevLockedRef = flake.lockedRef;
|
|
|
|
FlakeCache dummyCache;
|
2021-07-02 15:36:14 +03:00
|
|
|
flake = getFlake(state, topRef, useRegistries, dummyCache);
|
2021-01-27 15:02:54 +02:00
|
|
|
|
|
|
|
if (lockFlags.commitLockFile &&
|
|
|
|
flake.lockedRef.input.getRev() &&
|
|
|
|
prevLockedRef.input.getRev() != flake.lockedRef.input.getRev())
|
|
|
|
warn("committed new revision '%s'", flake.lockedRef.input.getRev()->gitRev());
|
|
|
|
|
|
|
|
/* Make sure that we picked up the change,
|
|
|
|
i.e. the tree should usually be dirty
|
|
|
|
now. Corner case: we could have reverted from a
|
|
|
|
dirty to a clean tree! */
|
|
|
|
if (flake.lockedRef.input == prevLockedRef.input
|
2022-02-24 19:09:00 +02:00
|
|
|
&& !flake.lockedRef.input.isLocked())
|
2021-01-27 15:02:54 +02:00
|
|
|
throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef);
|
2021-09-15 19:31:42 +03:00
|
|
|
} else {
|
2021-01-27 15:02:54 +02:00
|
|
|
warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
|
2021-09-15 19:31:42 +03:00
|
|
|
flake.forceDirty = true;
|
|
|
|
}
|
2020-03-27 22:08:41 +02:00
|
|
|
}
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 23:05:11 +02:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) };
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2021-01-27 15:02:54 +02:00
|
|
|
} catch (Error & e) {
|
|
|
|
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
|
|
|
|
throw;
|
|
|
|
}
|
2019-02-21 07:53:01 +02:00
|
|
|
}
|
|
|
|
|
2019-06-04 20:10:35 +03:00
|
|
|
void callFlake(EvalState & state,
|
2020-03-12 23:06:57 +02:00
|
|
|
const LockedFlake & lockedFlake,
|
2020-03-09 16:28:41 +02:00
|
|
|
Value & vRes)
|
2018-11-29 20:18:36 +02:00
|
|
|
{
|
2020-03-09 16:28:41 +02:00
|
|
|
auto vLocks = state.allocValue();
|
|
|
|
auto vRootSrc = state.allocValue();
|
2020-03-10 20:21:47 +02:00
|
|
|
auto vRootSubdir = state.allocValue();
|
|
|
|
auto vTmp1 = state.allocValue();
|
|
|
|
auto vTmp2 = state.allocValue();
|
2019-06-04 23:35:43 +03:00
|
|
|
|
2022-01-04 19:24:42 +02:00
|
|
|
vLocks->mkString(lockedFlake.lockFile.to_string());
|
2019-08-30 14:06:23 +03:00
|
|
|
|
2021-09-15 19:31:42 +03:00
|
|
|
emitTreeAttrs(
|
|
|
|
state,
|
|
|
|
*lockedFlake.flake.sourceInfo,
|
|
|
|
lockedFlake.flake.lockedRef.input,
|
|
|
|
*vRootSrc,
|
|
|
|
false,
|
|
|
|
lockedFlake.flake.forceDirty);
|
2019-10-02 23:08:19 +03:00
|
|
|
|
2022-01-04 19:24:42 +02:00
|
|
|
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
2020-03-10 20:21:47 +02:00
|
|
|
|
2021-08-29 19:55:38 +03:00
|
|
|
if (!state.vCallFlake) {
|
|
|
|
state.vCallFlake = allocRootValue(state.allocValue());
|
2020-03-11 17:34:46 +02:00
|
|
|
state.eval(state.parseExprFromString(
|
|
|
|
#include "call-flake.nix.gen.hh"
|
2023-04-06 14:15:50 +03:00
|
|
|
, CanonPath::root), **state.vCallFlake);
|
2020-03-11 17:34:46 +02:00
|
|
|
}
|
|
|
|
|
2021-08-29 19:55:38 +03:00
|
|
|
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);
|
2020-03-10 20:21:47 +02:00
|
|
|
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
|
|
|
|
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
2019-04-16 14:56:08 +03:00
|
|
|
}
|
2019-02-12 22:55:43 +02:00
|
|
|
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-02-12 22:55:43 +02:00
|
|
|
{
|
2023-01-19 14:23:04 +02:00
|
|
|
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
|
2020-04-27 23:53:11 +03:00
|
|
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
2022-02-24 19:09:00 +02:00
|
|
|
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
2022-03-04 20:31:59 +02:00
|
|
|
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
2020-04-03 14:07:05 +03:00
|
|
|
|
2020-01-29 22:01:34 +02:00
|
|
|
callFlake(state,
|
2020-04-03 14:07:05 +03:00
|
|
|
lockFlake(state, flakeRef,
|
2020-01-29 22:01:34 +02:00
|
|
|
LockFlags {
|
|
|
|
.updateLockFile = false,
|
2022-05-18 12:33:04 +03:00
|
|
|
.writeLockFile = false,
|
2022-03-01 03:29:34 +02:00
|
|
|
.useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries,
|
2022-12-07 13:58:58 +02:00
|
|
|
.allowUnlocked = !evalSettings.pureEval,
|
2020-01-29 22:01:34 +02:00
|
|
|
}),
|
|
|
|
v);
|
2018-11-29 20:18:36 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 15:04:18 +02:00
|
|
|
static RegisterPrimOp r2({
|
|
|
|
.name = "__getFlake",
|
|
|
|
.args = {"args"},
|
2022-03-25 15:19:55 +02:00
|
|
|
.doc = R"(
|
|
|
|
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
|
|
|
|
|
|
|
|
```nix
|
|
|
|
(builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix
|
|
|
|
```
|
|
|
|
|
|
|
|
Unless impure evaluation is allowed (`--impure`), the flake reference
|
|
|
|
must be "locked", e.g. contain a Git revision or content hash. An
|
|
|
|
example of an unlocked usage is:
|
|
|
|
|
|
|
|
```nix
|
|
|
|
(builtins.getFlake "github:edolstra/dwarffs").rev
|
|
|
|
```
|
|
|
|
)",
|
2022-03-25 15:04:18 +02:00
|
|
|
.fun = prim_getFlake,
|
|
|
|
.experimentalFeature = Xp::Flakes,
|
|
|
|
});
|
2018-11-29 20:18:36 +02:00
|
|
|
|
2023-07-25 20:43:33 +03:00
|
|
|
static void prim_parseFlakeRef(
|
|
|
|
EvalState & state,
|
|
|
|
const PosIdx pos,
|
|
|
|
Value * * args,
|
|
|
|
Value & v)
|
|
|
|
{
|
|
|
|
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos,
|
|
|
|
"while evaluating the argument passed to builtins.parseFlakeRef"));
|
|
|
|
auto attrs = parseFlakeRef(flakeRefS, {}, true).toAttrs();
|
|
|
|
auto binds = state.buildBindings(attrs.size());
|
|
|
|
for (const auto & [key, value] : attrs) {
|
|
|
|
auto s = state.symbols.create(key);
|
|
|
|
auto & vv = binds.alloc(s);
|
|
|
|
std::visit(overloaded {
|
|
|
|
[&vv](const std::string & value) { vv.mkString(value); },
|
|
|
|
[&vv](const uint64_t & value) { vv.mkInt(value); },
|
|
|
|
[&vv](const Explicit<bool> & value) { vv.mkBool(value.t); }
|
|
|
|
}, value);
|
|
|
|
}
|
|
|
|
v.mkAttrs(binds);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RegisterPrimOp r3({
|
|
|
|
.name = "__parseFlakeRef",
|
|
|
|
.args = {"flake-ref"},
|
|
|
|
.doc = R"(
|
|
|
|
Parse a flake reference, and return its exploded form.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
```nix
|
|
|
|
builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib"
|
|
|
|
```
|
|
|
|
evaluates to:
|
|
|
|
```nix
|
|
|
|
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
|
|
|
|
```
|
|
|
|
)",
|
|
|
|
.fun = prim_parseFlakeRef,
|
|
|
|
.experimentalFeature = Xp::Flakes,
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
static void prim_flakeRefToString(
|
|
|
|
EvalState & state,
|
|
|
|
const PosIdx pos,
|
|
|
|
Value * * args,
|
|
|
|
Value & v)
|
|
|
|
{
|
|
|
|
state.forceAttrs(*args[0], noPos,
|
|
|
|
"while evaluating the argument passed to builtins.flakeRefToString");
|
|
|
|
fetchers::Attrs attrs;
|
|
|
|
for (const auto & attr : *args[0]->attrs) {
|
|
|
|
auto t = attr.value->type();
|
|
|
|
if (t == nInt) {
|
|
|
|
attrs.emplace(state.symbols[attr.name],
|
|
|
|
(uint64_t) attr.value->integer);
|
|
|
|
} else if (t == nBool) {
|
|
|
|
attrs.emplace(state.symbols[attr.name],
|
|
|
|
Explicit<bool> { attr.value->boolean });
|
|
|
|
} else if (t == nString) {
|
|
|
|
attrs.emplace(state.symbols[attr.name],
|
|
|
|
std::string(attr.value->str()));
|
|
|
|
} else {
|
|
|
|
state.error(
|
|
|
|
"flake reference attribute sets may only contain integers, Booleans, "
|
|
|
|
"and strings, but attribute '%s' is %s",
|
|
|
|
state.symbols[attr.name],
|
|
|
|
showType(*attr.value)).debugThrow<EvalError>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto flakeRef = FlakeRef::fromAttrs(attrs);
|
|
|
|
v.mkString(flakeRef.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
static RegisterPrimOp r4({
|
|
|
|
.name = "__flakeRefToString",
|
|
|
|
.args = {"attrs"},
|
|
|
|
.doc = R"(
|
|
|
|
Convert a flake reference from attribute set format to URL format.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
```nix
|
|
|
|
builtins.flakeRefToString {
|
|
|
|
dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github";
|
|
|
|
}
|
|
|
|
```
|
|
|
|
evaluates to
|
|
|
|
```nix
|
|
|
|
"github:NixOS/nixpkgs/23.05?dir=lib"
|
|
|
|
```
|
|
|
|
)",
|
|
|
|
.fun = prim_flakeRefToString,
|
|
|
|
.experimentalFeature = Xp::Flakes,
|
|
|
|
});
|
|
|
|
|
2019-05-29 16:31:07 +03:00
|
|
|
}
|
|
|
|
|
2020-01-22 21:59:59 +02:00
|
|
|
Fingerprint LockedFlake::getFingerprint() const
|
2019-06-07 23:25:48 +03:00
|
|
|
{
|
2019-07-12 14:29:54 +03:00
|
|
|
// FIXME: as an optimization, if the flake contains a lock file
|
|
|
|
// and we haven't changed it, then it's sufficient to use
|
2019-06-07 23:25:48 +03:00
|
|
|
// flake.sourceInfo.storePath for the fingerprint.
|
|
|
|
return hashString(htSHA256,
|
2021-09-17 00:58:21 +03:00
|
|
|
fmt("%s;%s;%d;%d;%s",
|
2020-01-21 17:27:53 +02:00
|
|
|
flake.sourceInfo->storePath.to_string(),
|
2021-09-17 00:58:21 +03:00
|
|
|
flake.lockedRef.subdir,
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
flake.lockedRef.input.getRevCount().value_or(0),
|
|
|
|
flake.lockedRef.input.getLastModified().value_or(0),
|
2019-11-06 13:01:37 +02:00
|
|
|
lockFile));
|
2019-06-07 23:25:48 +03:00
|
|
|
}
|
|
|
|
|
2020-01-21 17:27:53 +02:00
|
|
|
Flake::~Flake() { }
|
|
|
|
|
2019-05-29 16:31:07 +03:00
|
|
|
}
|