No global settings in libnixfetchers and libnixflake

Progress on #5638

There are still a global fetcher and eval settings, but they are pushed
down into `libnixcmd`, which is a lot less bad a place for this sort of
thing.

Continuing process pioneered in
52bfccf8d8.
This commit is contained in:
John Ericson 2024-07-01 13:37:30 -04:00
parent b57c361097
commit 3fc77f281e
50 changed files with 401 additions and 271 deletions

View File

@ -132,7 +132,7 @@ ref<EvalState> EvalCommand::getEvalState()
#else #else
std::make_shared<EvalState>( std::make_shared<EvalState>(
#endif #endif
lookupPath, getEvalStore(), evalSettings, getStore()) lookupPath, getEvalStore(), fetchSettings, evalSettings, getStore())
; ;
evalState->repair = repair; evalState->repair = repair;

View File

@ -1,3 +1,4 @@
#include "fetch-settings.hh"
#include "eval-settings.hh" #include "eval-settings.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "shared.hh" #include "shared.hh"
@ -7,6 +8,7 @@
#include "fetchers.hh" #include "fetchers.hh"
#include "registry.hh" #include "registry.hh"
#include "flake/flakeref.hh" #include "flake/flakeref.hh"
#include "flake/settings.hh"
#include "store-api.hh" #include "store-api.hh"
#include "command.hh" #include "command.hh"
#include "tarball.hh" #include "tarball.hh"
@ -16,6 +18,10 @@
namespace nix { namespace nix {
fetchers::Settings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings);
EvalSettings evalSettings { EvalSettings evalSettings {
settings.readOnlyMode, settings.readOnlyMode,
{ {
@ -24,7 +30,7 @@ EvalSettings evalSettings {
[](ref<Store> store, std::string_view rest) { [](ref<Store> store, std::string_view rest) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
// FIXME `parseFlakeRef` should take a `std::string_view`. // FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(std::string { rest }, {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false);
debug("fetching flake search path element '%s''", rest); debug("fetching flake search path element '%s''", rest);
auto storePath = flakeRef.resolve(store).fetchTree(store).first; auto storePath = flakeRef.resolve(store).fetchTree(store).first;
return store->toRealPath(storePath); return store->toRealPath(storePath);
@ -35,6 +41,12 @@ EvalSettings evalSettings {
static GlobalConfig::Register rEvalSettings(&evalSettings); static GlobalConfig::Register rEvalSettings(&evalSettings);
flake::Settings flakeSettings;
static GlobalConfig::Register rFlakeSettings(&flakeSettings);
CompatibilitySettings compatibilitySettings {}; CompatibilitySettings compatibilitySettings {};
static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings);
@ -171,8 +183,8 @@ MixEvalArgs::MixEvalArgs()
.category = category, .category = category,
.labels = {"original-ref", "resolved-ref"}, .labels = {"original-ref", "resolved-ref"},
.handler = {[&](std::string _from, std::string _to) { .handler = {[&](std::string _from, std::string _to) {
auto from = parseFlakeRef(_from, absPath(".")); auto from = parseFlakeRef(fetchSettings, _from, absPath("."));
auto to = parseFlakeRef(_to, absPath(".")); auto to = parseFlakeRef(fetchSettings, _to, absPath("."));
fetchers::Attrs extraAttrs; fetchers::Attrs extraAttrs;
if (to.subdir != "") extraAttrs["dir"] = to.subdir; if (to.subdir != "") extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs); fetchers::overrideRegistry(from.input, to.input, extraAttrs);
@ -230,7 +242,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
else if (hasPrefix(s, "flake:")) { else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first; auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first;
return state.rootPath(CanonPath(state.store->toRealPath(storePath))); return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
} }

View File

@ -11,17 +11,32 @@
namespace nix { namespace nix {
class Store; class Store;
namespace fetchers { struct Settings; }
class EvalState; class EvalState;
struct EvalSettings; struct EvalSettings;
struct CompatibilitySettings; struct CompatibilitySettings;
class Bindings; class Bindings;
struct SourcePath; struct SourcePath;
namespace flake { struct Settings; }
/**
* @todo Get rid of global setttings variables
*/
extern fetchers::Settings fetchSettings;
/** /**
* @todo Get rid of global setttings variables * @todo Get rid of global setttings variables
*/ */
extern EvalSettings evalSettings; extern EvalSettings evalSettings;
/**
* @todo Get rid of global setttings variables
*/
extern flake::Settings flakeSettings;
/** /**
* Settings that control behaviors that have changed since Nix 2.3. * Settings that control behaviors that have changed since Nix 2.3.
*/ */

View File

@ -196,7 +196,8 @@ std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
flake::LockFlags lockFlagsApplyConfig = lockFlags; flake::LockFlags lockFlagsApplyConfig = lockFlags;
// FIXME why this side effect? // FIXME why this side effect?
lockFlagsApplyConfig.applyNixConfig = true; lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(
flakeSettings, *state, flakeRef, lockFlagsApplyConfig));
} }
return _lockedFlake; return _lockedFlake;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
///@file ///@file
#include "common-eval-args.hh"
#include "installable-value.hh" #include "installable-value.hh"
namespace nix { namespace nix {
@ -78,7 +79,7 @@ struct InstallableFlake : InstallableValue
*/ */
static inline FlakeRef defaultNixpkgsFlakeRef() static inline FlakeRef defaultNixpkgsFlakeRef()
{ {
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); return FlakeRef::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", "nixpkgs"}});
} }
ref<eval_cache::EvalCache> openEvalCache( ref<eval_cache::EvalCache> openEvalCache(

View File

@ -129,7 +129,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false; lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign( lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath), flake::parseInputPath(inputPath),
parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true)); parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()), true));
}}, }},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) { if (n == 0) {
@ -170,14 +170,15 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {[&](std::string flakeRef) { .handler = {[&](std::string flakeRef) {
auto evalState = getEvalState(); auto evalState = getEvalState();
auto flake = flake::lockFlake( auto flake = flake::lockFlake(
flakeSettings,
*evalState, *evalState,
parseFlakeRef(flakeRef, absPath(getCommandBaseDir())), parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())),
{ .writeLockFile = false }); { .writeLockFile = false });
for (auto & [inputName, input] : flake.lockFile.root->inputs) { for (auto & [inputName, input] : flake.lockFile.root->inputs) {
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) { if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) {
overrideRegistry( overrideRegistry(
fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), fetchers::Input::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", inputName}}),
input3->lockedRef.input, input3->lockedRef.input,
{}); {});
} }
@ -338,10 +339,11 @@ void completeFlakeRefWithFragment(
auto flakeRefS = std::string(prefix.substr(0, hash)); auto flakeRefS = std::string(prefix.substr(0, hash));
// TODO: ideally this would use the command base directory instead of assuming ".". // TODO: ideally this would use the command base directory instead of assuming ".".
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), absPath("."));
auto evalCache = openEvalCache(*evalState, auto evalCache = openEvalCache(*evalState,
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags))); std::make_shared<flake::LockedFlake>(lockFlake(
flakeSettings, *evalState, flakeRef, lockFlags)));
auto root = evalCache->getRoot(); auto root = evalCache->getRoot();
@ -403,7 +405,7 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
Args::completeDir(completions, 0, prefix); Args::completeDir(completions, 0, prefix);
/* Look for registry entries that match the prefix. */ /* Look for registry entries that match the prefix. */
for (auto & registry : fetchers::getRegistries(store)) { for (auto & registry : fetchers::getRegistries(fetchSettings, store)) {
for (auto & entry : registry->entries) { for (auto & entry : registry->entries) {
auto from = entry.from.to_string(); auto from = entry.from.to_string();
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
@ -534,7 +536,8 @@ Installables SourceExprCommand::parseInstallables(
} }
try { try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir())); auto [flakeRef, fragment] = parseFlakeRefWithFragment(
fetchSettings, std::string { prefix }, absPath(getCommandBaseDir()));
result.push_back(make_ref<InstallableFlake>( result.push_back(make_ref<InstallableFlake>(
this, this,
getEvalState(), getEvalState(),
@ -851,6 +854,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
std::vector<FlakeRef> res; std::vector<FlakeRef> res;
for (auto i : rawInstallables) for (auto i : rawInstallables)
res.push_back(parseFlakeRefWithFragment( res.push_back(parseFlakeRefWithFragment(
fetchSettings,
expandTilde(i), expandTilde(i),
absPath(getCommandBaseDir())).first); absPath(getCommandBaseDir())).first);
return res; return res;
@ -873,6 +877,7 @@ std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
{ {
return { return {
parseFlakeRefWithFragment( parseFlakeRefWithFragment(
fetchSettings,
expandTilde(_installable), expandTilde(_installable),
absPath(getCommandBaseDir())).first absPath(getCommandBaseDir())).first
}; };

View File

@ -690,14 +690,14 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
if (flakeRefS.empty()) if (flakeRefS.empty())
throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)"); throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)");
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true); auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, absPath("."), true);
if (evalSettings.pureEval && !flakeRef.input.isLocked()) if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
Value v; Value v;
flake::callFlake(*state, flake::callFlake(*state,
flake::lockFlake(*state, flakeRef, flake::lockFlake(flakeSettings, *state, flakeRef,
flake::LockFlags { flake::LockFlags {
.updateLockFile = false, .updateLockFile = false,
.useRegistries = !evalSettings.pureEval, .useRegistries = !evalSettings.pureEval,

View File

@ -15,7 +15,7 @@ libexprc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \
$(INCLUDE_libstore) $(INCLUDE_libstorec) \ $(INCLUDE_libstore) $(INCLUDE_libstorec) \
$(INCLUDE_libexpr) $(INCLUDE_libexprc) $(INCLUDE_libexpr) $(INCLUDE_libexprc)
libexprc_LIBS = libutil libutilc libstore libstorec libexpr libexprc_LIBS = libutil libutilc libstore libstorec libfetchers libexpr
libexprc_LDFLAGS += $(THREAD_LDFLAGS) libexprc_LDFLAGS += $(THREAD_LDFLAGS)

View File

@ -112,12 +112,14 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
static_cast<std::align_val_t>(alignof(EvalState))); static_cast<std::align_val_t>(alignof(EvalState)));
auto * p2 = static_cast<EvalState *>(p); auto * p2 = static_cast<EvalState *>(p);
new (p) EvalState { new (p) EvalState {
.fetchSettings = nix::fetchers::Settings{},
.settings = nix::EvalSettings{ .settings = nix::EvalSettings{
nix::settings.readOnlyMode, nix::settings.readOnlyMode,
}, },
.state = nix::EvalState( .state = nix::EvalState(
nix::LookupPath::parse(lookupPath), nix::LookupPath::parse(lookupPath),
store->ptr, store->ptr,
p2->fetchSettings,
p2->settings), p2->settings),
}; };
loadConfFile(p2->settings); loadConfFile(p2->settings);

View File

@ -1,6 +1,7 @@
#ifndef NIX_API_EXPR_INTERNAL_H #ifndef NIX_API_EXPR_INTERNAL_H
#define NIX_API_EXPR_INTERNAL_H #define NIX_API_EXPR_INTERNAL_H
#include "fetch-settings.hh"
#include "eval.hh" #include "eval.hh"
#include "eval-settings.hh" #include "eval-settings.hh"
#include "attr-set.hh" #include "attr-set.hh"
@ -8,6 +9,7 @@
struct EvalState struct EvalState
{ {
nix::fetchers::Settings fetchSettings;
nix::EvalSettings settings; nix::EvalSettings settings;
nix::EvalState state; nix::EvalState state;
}; };

View File

@ -1,5 +1,4 @@
#include "users.hh" #include "users.hh"
#include "config-global.hh"
#include "globals.hh" #include "globals.hh"
#include "profiles.hh" #include "profiles.hh"
#include "eval.hh" #include "eval.hh"

View File

@ -217,9 +217,11 @@ static constexpr size_t BASE_ENV_SIZE = 128;
EvalState::EvalState( EvalState::EvalState(
const LookupPath & _lookupPath, const LookupPath & _lookupPath,
ref<Store> store, ref<Store> store,
const fetchers::Settings & fetchSettings,
const EvalSettings & settings, const EvalSettings & settings,
std::shared_ptr<Store> buildStore) std::shared_ptr<Store> buildStore)
: settings{settings} : fetchSettings{fetchSettings}
, settings{settings}
, sWith(symbols.create("<with>")) , sWith(symbols.create("<with>"))
, sOutPath(symbols.create("outPath")) , sOutPath(symbols.create("outPath"))
, sDrvPath(symbols.create("drvPath")) , sDrvPath(symbols.create("drvPath"))

View File

@ -30,6 +30,7 @@ namespace nix {
constexpr size_t maxPrimOpArity = 8; constexpr size_t maxPrimOpArity = 8;
class Store; class Store;
namespace fetchers { struct Settings; }
struct EvalSettings; struct EvalSettings;
class EvalState; class EvalState;
class StorePath; class StorePath;
@ -43,7 +44,7 @@ namespace eval_cache {
/** /**
* Function that implements a primop. * Function that implements a primop.
*/ */
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v); using PrimOpFun = void(EvalState & state, const PosIdx pos, Value * * args, Value & v);
/** /**
* Info about a primitive operation, and its implementation * Info about a primitive operation, and its implementation
@ -84,7 +85,7 @@ struct PrimOp
/** /**
* Implementation of the primop. * Implementation of the primop.
*/ */
std::function<std::remove_pointer<PrimOpFun>::type> fun; std::function<PrimOpFun> fun;
/** /**
* Optional experimental for this to be gated on. * Optional experimental for this to be gated on.
@ -162,6 +163,7 @@ struct DebugTrace {
class EvalState : public std::enable_shared_from_this<EvalState> class EvalState : public std::enable_shared_from_this<EvalState>
{ {
public: public:
const fetchers::Settings & fetchSettings;
const EvalSettings & settings; const EvalSettings & settings;
SymbolTable symbols; SymbolTable symbols;
PosTable positions; PosTable positions;
@ -353,6 +355,7 @@ public:
EvalState( EvalState(
const LookupPath & _lookupPath, const LookupPath & _lookupPath,
ref<Store> store, ref<Store> store,
const fetchers::Settings & fetchSettings,
const EvalSettings & settings, const EvalSettings & settings,
std::shared_ptr<Store> buildStore = nullptr); std::shared_ptr<Store> buildStore = nullptr);
~EvalState(); ~EvalState();

View File

@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
attrs.insert_or_assign("name", std::string(name)); attrs.insert_or_assign("name", std::string(name));
if (ref) attrs.insert_or_assign("ref", *ref); if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev()); if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs)); auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
auto [storePath, input2] = input.fetchToStore(state.store); auto [storePath, input2] = input.fetchToStore(state.store);

View File

@ -85,7 +85,7 @@ static void fetchTree(
Value & v, Value & v,
const FetchTreeParams & params = FetchTreeParams{} const FetchTreeParams & params = FetchTreeParams{}
) { ) {
fetchers::Input input; fetchers::Input input { state.fetchSettings };
NixStringContext context; NixStringContext context;
std::optional<std::string> type; std::optional<std::string> type;
if (params.isFetchGit) type = "git"; if (params.isFetchGit) type = "git";
@ -148,7 +148,7 @@ static void fetchTree(
"attribute 'name' isnt supported in call to 'fetchTree'" "attribute 'name' isnt supported in call to 'fetchTree'"
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
input = fetchers::Input::fromAttrs(std::move(attrs)); input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else { } else {
auto url = state.coerceToString(pos, *args[0], context, auto url = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to the fetcher", "while evaluating the first argument passed to the fetcher",
@ -161,13 +161,13 @@ static void fetchTree(
if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) { if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
attrs.emplace("exportIgnore", Explicit<bool>{true}); attrs.emplace("exportIgnore", Explicit<bool>{true});
} }
input = fetchers::Input::fromAttrs(std::move(attrs)); input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else { } else {
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes)) if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
state.error<EvalError>( state.error<EvalError>(
"passing a string argument to 'fetchTree' requires the 'flakes' experimental feature" "passing a string argument to 'fetchTree' requires the 'flakes' experimental feature"
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
input = fetchers::Input::fromURL(url); input = fetchers::Input::fromURL(state.fetchSettings, url);
} }
} }

View File

@ -1,14 +1,9 @@
#include "fetch-settings.hh" #include "fetch-settings.hh"
#include "config-global.hh"
namespace nix { namespace nix::fetchers {
FetchSettings::FetchSettings() Settings::Settings()
{ {
} }
FetchSettings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings);
} }

View File

@ -9,11 +9,11 @@
#include <sys/types.h> #include <sys/types.h>
namespace nix { namespace nix::fetchers {
struct FetchSettings : public Config struct Settings : public Config
{ {
FetchSettings(); Settings();
Setting<StringMap> accessTokens{this, {}, "access-tokens", Setting<StringMap> accessTokens{this, {}, "access-tokens",
R"( R"(
@ -84,9 +84,14 @@ struct FetchSettings : public Config
`narHash` attribute is specified, `narHash` attribute is specified,
e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`.
)"}; )"};
Setting<std::string> flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry",
R"(
Path or URI of the global flake registry.
When empty, disables the global flake registry.
)",
{}, true, Xp::Flakes};
}; };
// FIXME: don't use a global variable.
extern FetchSettings fetchSettings;
} }

View File

@ -35,9 +35,11 @@ nlohmann::json dumpRegisterInputSchemeInfo() {
return res; return res;
} }
Input Input::fromURL(const std::string & url, bool requireTree) Input Input::fromURL(
const Settings & settings,
const std::string & url, bool requireTree)
{ {
return fromURL(parseURL(url), requireTree); return fromURL(settings, parseURL(url), requireTree);
} }
static void fixupInput(Input & input) static void fixupInput(Input & input)
@ -49,10 +51,12 @@ static void fixupInput(Input & input)
input.getLastModified(); input.getLastModified();
} }
Input Input::fromURL(const ParsedURL & url, bool requireTree) Input Input::fromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree)
{ {
for (auto & [_, inputScheme] : *inputSchemes) { for (auto & [_, inputScheme] : *inputSchemes) {
auto res = inputScheme->inputFromURL(url, requireTree); auto res = inputScheme->inputFromURL(settings, url, requireTree);
if (res) { if (res) {
experimentalFeatureSettings.require(inputScheme->experimentalFeature()); experimentalFeatureSettings.require(inputScheme->experimentalFeature());
res->scheme = inputScheme; res->scheme = inputScheme;
@ -64,7 +68,7 @@ Input Input::fromURL(const ParsedURL & url, bool requireTree)
throw Error("input '%s' is unsupported", url.url); throw Error("input '%s' is unsupported", url.url);
} }
Input Input::fromAttrs(Attrs && attrs) Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
{ {
auto schemeName = ({ auto schemeName = ({
auto schemeNameOpt = maybeGetStrAttr(attrs, "type"); auto schemeNameOpt = maybeGetStrAttr(attrs, "type");
@ -78,7 +82,7 @@ Input Input::fromAttrs(Attrs && attrs)
// but not all of them. Doing this is to support those other // but not all of them. Doing this is to support those other
// operations which are supposed to be robust on // operations which are supposed to be robust on
// unknown/uninterpretable inputs. // unknown/uninterpretable inputs.
Input input; Input input { settings };
input.attrs = attrs; input.attrs = attrs;
fixupInput(input); fixupInput(input);
return input; return input;
@ -99,7 +103,7 @@ Input Input::fromAttrs(Attrs && attrs)
if (name != "type" && allowedAttrs.count(name) == 0) if (name != "type" && allowedAttrs.count(name) == 0)
throw Error("input attribute '%s' not supported by scheme '%s'", name, schemeName); throw Error("input attribute '%s' not supported by scheme '%s'", name, schemeName);
auto res = inputScheme->inputFromAttrs(attrs); auto res = inputScheme->inputFromAttrs(settings, attrs);
if (!res) return raw(); if (!res) return raw();
res->scheme = inputScheme; res->scheme = inputScheme;
fixupInput(*res); fixupInput(*res);

View File

@ -17,6 +17,8 @@ namespace nix::fetchers {
struct InputScheme; struct InputScheme;
struct Settings;
/** /**
* The `Input` object is generated by a specific fetcher, based on * The `Input` object is generated by a specific fetcher, based on
* user-supplied information, and contains * user-supplied information, and contains
@ -28,6 +30,12 @@ struct Input
{ {
friend struct InputScheme; friend struct InputScheme;
const Settings * settings;
Input(const Settings & settings)
: settings{&settings}
{ }
std::shared_ptr<InputScheme> scheme; // note: can be null std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs; Attrs attrs;
@ -42,16 +50,22 @@ public:
* *
* The URL indicate which sort of fetcher, and provides information to that fetcher. * The URL indicate which sort of fetcher, and provides information to that fetcher.
*/ */
static Input fromURL(const std::string & url, bool requireTree = true); static Input fromURL(
const Settings & settings,
const std::string & url, bool requireTree = true);
static Input fromURL(const ParsedURL & url, bool requireTree = true); static Input fromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree = true);
/** /**
* Create an `Input` from a an `Attrs`. * Create an `Input` from a an `Attrs`.
* *
* The URL indicate which sort of fetcher, and provides information to that fetcher. * The URL indicate which sort of fetcher, and provides information to that fetcher.
*/ */
static Input fromAttrs(Attrs && attrs); static Input fromAttrs(
const Settings & settings,
Attrs && attrs);
ParsedURL toURL() const; ParsedURL toURL() const;
@ -146,9 +160,13 @@ struct InputScheme
virtual ~InputScheme() virtual ~InputScheme()
{ } { }
virtual std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const = 0; virtual std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree) const = 0;
virtual std::optional<Input> inputFromAttrs(const Attrs & attrs) const = 0; virtual std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const = 0;
/** /**
* What is the name of the scheme? * What is the name of the scheme?

View File

@ -164,7 +164,9 @@ static const Hash nullRev{HashAlgorithm::SHA1};
struct GitInputScheme : InputScheme struct GitInputScheme : InputScheme
{ {
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree) const override
{ {
if (url.scheme != "git" && if (url.scheme != "git" &&
url.scheme != "git+http" && url.scheme != "git+http" &&
@ -190,7 +192,7 @@ struct GitInputScheme : InputScheme
attrs.emplace("url", url2.to_string()); attrs.emplace("url", url2.to_string());
return inputFromAttrs(attrs); return inputFromAttrs(settings, attrs);
} }
@ -222,7 +224,9 @@ struct GitInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const override
{ {
for (auto & [name, _] : attrs) for (auto & [name, _] : attrs)
if (name == "verifyCommit" if (name == "verifyCommit"
@ -238,7 +242,7 @@ struct GitInputScheme : InputScheme
throw BadURL("invalid Git branch/tag name '%s'", *ref); throw BadURL("invalid Git branch/tag name '%s'", *ref);
} }
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
auto url = fixGitURL(getStrAttr(attrs, "url")); auto url = fixGitURL(getStrAttr(attrs, "url"));
parseURL(url); parseURL(url);
@ -366,13 +370,13 @@ struct GitInputScheme : InputScheme
/* URL of the repo, or its path if isLocal. Never a `file` URL. */ /* URL of the repo, or its path if isLocal. Never a `file` URL. */
std::string url; std::string url;
void warnDirty() const void warnDirty(const Settings & settings) const
{ {
if (workdirInfo.isDirty) { if (workdirInfo.isDirty) {
if (!fetchSettings.allowDirty) if (!settings.allowDirty)
throw Error("Git tree '%s' is dirty", url); throw Error("Git tree '%s' is dirty", url);
if (fetchSettings.warnDirty) if (settings.warnDirty)
warn("Git tree '%s' is dirty", url); warn("Git tree '%s' is dirty", url);
} }
} }
@ -653,7 +657,7 @@ struct GitInputScheme : InputScheme
attrs.insert_or_assign("exportIgnore", Explicit<bool>{ exportIgnore }); attrs.insert_or_assign("exportIgnore", Explicit<bool>{ exportIgnore });
attrs.insert_or_assign("submodules", Explicit<bool>{ true }); attrs.insert_or_assign("submodules", Explicit<bool>{ true });
attrs.insert_or_assign("allRefs", Explicit<bool>{ true }); attrs.insert_or_assign("allRefs", Explicit<bool>{ true });
auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = auto [submoduleAccessor, submoduleInput2] =
submoduleInput.getAccessor(store); submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
@ -711,7 +715,7 @@ struct GitInputScheme : InputScheme
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out // TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
// attrs.insert_or_assign("allRefs", Explicit<bool>{ true }); // attrs.insert_or_assign("allRefs", Explicit<bool>{ true });
auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs));
auto [submoduleAccessor, submoduleInput2] = auto [submoduleAccessor, submoduleInput2] =
submoduleInput.getAccessor(store); submoduleInput.getAccessor(store);
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
@ -743,7 +747,7 @@ struct GitInputScheme : InputScheme
verifyCommit(input, repo); verifyCommit(input, repo);
} else { } else {
repoInfo.warnDirty(); repoInfo.warnDirty(*input.settings);
if (repoInfo.workdirInfo.headRev) { if (repoInfo.workdirInfo.headRev) {
input.attrs.insert_or_assign("dirtyRev", input.attrs.insert_or_assign("dirtyRev",

View File

@ -31,7 +31,9 @@ struct GitArchiveInputScheme : InputScheme
{ {
virtual std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const = 0; virtual std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const = 0;
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override std::optional<Input> inputFromURL(
const fetchers::Settings & settings,
const ParsedURL & url, bool requireTree) const override
{ {
if (url.scheme != schemeName()) return {}; if (url.scheme != schemeName()) return {};
@ -90,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme
if (ref && rev) if (ref && rev)
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev()); throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev());
Input input; Input input{settings};
input.attrs.insert_or_assign("type", std::string { schemeName() }); input.attrs.insert_or_assign("type", std::string { schemeName() });
input.attrs.insert_or_assign("owner", path[0]); input.attrs.insert_or_assign("owner", path[0]);
input.attrs.insert_or_assign("repo", path[1]); input.attrs.insert_or_assign("repo", path[1]);
@ -119,12 +121,14 @@ struct GitArchiveInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const fetchers::Settings & settings,
const Attrs & attrs) const override
{ {
getStrAttr(attrs, "owner"); getStrAttr(attrs, "owner");
getStrAttr(attrs, "repo"); getStrAttr(attrs, "repo");
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
return input; return input;
} }
@ -168,18 +172,20 @@ struct GitArchiveInputScheme : InputScheme
return input; return input;
} }
std::optional<std::string> getAccessToken(const std::string & host) const std::optional<std::string> getAccessToken(const fetchers::Settings & settings, const std::string & host) const
{ {
auto tokens = fetchSettings.accessTokens.get(); auto tokens = settings.accessTokens.get();
if (auto token = get(tokens, host)) if (auto token = get(tokens, host))
return *token; return *token;
return {}; return {};
} }
Headers makeHeadersWithAuthTokens(const std::string & host) const Headers makeHeadersWithAuthTokens(
const fetchers::Settings & settings,
const std::string & host) const
{ {
Headers headers; Headers headers;
auto accessToken = getAccessToken(host); auto accessToken = getAccessToken(settings, host);
if (accessToken) { if (accessToken) {
auto hdr = accessHeaderFromToken(*accessToken); auto hdr = accessHeaderFromToken(*accessToken);
if (hdr) if (hdr)
@ -295,7 +301,7 @@ struct GitArchiveInputScheme : InputScheme
locking. FIXME: in the future, we may want to require a Git locking. FIXME: in the future, we may want to require a Git
tree hash instead of a NAR hash. */ tree hash instead of a NAR hash. */
return input.getRev().has_value() return input.getRev().has_value()
&& (fetchSettings.trustTarballsFromGitForges || && (input.settings->trustTarballsFromGitForges ||
input.getNarHash().has_value()); input.getNarHash().has_value());
} }
@ -352,7 +358,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
: "https://%s/api/v3/repos/%s/%s/commits/%s", : "https://%s/api/v3/repos/%s/%s/commits/%s",
host, getOwner(input), getRepo(input), *input.getRef()); host, getOwner(input), getRepo(input), *input.getRef());
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
auto json = nlohmann::json::parse( auto json = nlohmann::json::parse(
readFile( readFile(
@ -369,7 +375,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
{ {
auto host = getHost(input); auto host = getHost(input);
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
// If we have no auth headers then we default to the public archive // If we have no auth headers then we default to the public archive
// urls so we do not run into rate limits. // urls so we do not run into rate limits.
@ -389,7 +395,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
void clone(const Input & input, const Path & destDir) const override void clone(const Input & input, const Path & destDir) const override
{ {
auto host = getHost(input); auto host = getHost(input);
Input::fromURL(fmt("git+https://%s/%s/%s.git", Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git",
host, getOwner(input), getRepo(input))) host, getOwner(input), getRepo(input)))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(destDir); .clone(destDir);
@ -426,7 +432,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s", auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef()); host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
auto json = nlohmann::json::parse( auto json = nlohmann::json::parse(
readFile( readFile(
@ -456,7 +462,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false)); input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
return DownloadUrl { url, headers }; return DownloadUrl { url, headers };
} }
@ -464,7 +470,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
{ {
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
// FIXME: get username somewhere // FIXME: get username somewhere
Input::fromURL(fmt("git+https://%s/%s/%s.git", Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(destDir); .clone(destDir);
@ -496,7 +502,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
auto base_url = fmt("https://%s/%s/%s", auto base_url = fmt("https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")); host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
std::string refUri; std::string refUri;
if (ref == "HEAD") { if (ref == "HEAD") {
@ -543,14 +549,14 @@ struct SourceHutInputScheme : GitArchiveInputScheme
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(HashFormat::Base16, false)); input.getRev()->to_string(HashFormat::Base16, false));
Headers headers = makeHeadersWithAuthTokens(host); Headers headers = makeHeadersWithAuthTokens(*input.settings, host);
return DownloadUrl { url, headers }; return DownloadUrl { url, headers };
} }
void clone(const Input & input, const Path & destDir) const override void clone(const Input & input, const Path & destDir) const override
{ {
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(fmt("git+https://%s/%s/%s", Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev()) .applyOverrides(input.getRef(), input.getRev())
.clone(destDir); .clone(destDir);

View File

@ -8,7 +8,9 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript);
struct IndirectInputScheme : InputScheme struct IndirectInputScheme : InputScheme
{ {
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree) const override
{ {
if (url.scheme != "flake") return {}; if (url.scheme != "flake") return {};
@ -41,7 +43,7 @@ struct IndirectInputScheme : InputScheme
// FIXME: forbid query params? // FIXME: forbid query params?
Input input; Input input{settings};
input.attrs.insert_or_assign("type", "indirect"); input.attrs.insert_or_assign("type", "indirect");
input.attrs.insert_or_assign("id", id); input.attrs.insert_or_assign("id", id);
if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); if (rev) input.attrs.insert_or_assign("rev", rev->gitRev());
@ -65,13 +67,15 @@ struct IndirectInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const override
{ {
auto id = getStrAttr(attrs, "id"); auto id = getStrAttr(attrs, "id");
if (!std::regex_match(id, flakeRegex)) if (!std::regex_match(id, flakeRegex))
throw BadURL("'%s' is not a valid flake ID", id); throw BadURL("'%s' is not a valid flake ID", id);
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
return input; return input;
} }

View File

@ -45,7 +45,9 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
struct MercurialInputScheme : InputScheme struct MercurialInputScheme : InputScheme
{ {
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree) const override
{ {
if (url.scheme != "hg+http" && if (url.scheme != "hg+http" &&
url.scheme != "hg+https" && url.scheme != "hg+https" &&
@ -68,7 +70,7 @@ struct MercurialInputScheme : InputScheme
attrs.emplace("url", url2.to_string()); attrs.emplace("url", url2.to_string());
return inputFromAttrs(attrs); return inputFromAttrs(settings, attrs);
} }
std::string_view schemeName() const override std::string_view schemeName() const override
@ -88,7 +90,9 @@ struct MercurialInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const override
{ {
parseURL(getStrAttr(attrs, "url")); parseURL(getStrAttr(attrs, "url"));
@ -97,7 +101,7 @@ struct MercurialInputScheme : InputScheme
throw BadURL("invalid Mercurial branch/tag name '%s'", *ref); throw BadURL("invalid Mercurial branch/tag name '%s'", *ref);
} }
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
return input; return input;
} }
@ -182,10 +186,10 @@ struct MercurialInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked /* This is an unclean working tree. So copy all tracked
files. */ files. */
if (!fetchSettings.allowDirty) if (!input.settings->allowDirty)
throw Error("Mercurial tree '%s' is unclean", actualUrl); throw Error("Mercurial tree '%s' is unclean", actualUrl);
if (fetchSettings.warnDirty) if (input.settings->warnDirty)
warn("Mercurial tree '%s' is unclean", actualUrl); warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl }))); input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));

View File

@ -7,14 +7,16 @@ namespace nix::fetchers {
struct PathInputScheme : InputScheme struct PathInputScheme : InputScheme
{ {
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & url, bool requireTree) const override
{ {
if (url.scheme != "path") return {}; if (url.scheme != "path") return {};
if (url.authority && *url.authority != "") if (url.authority && *url.authority != "")
throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority); throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority);
Input input; Input input{settings};
input.attrs.insert_or_assign("type", "path"); input.attrs.insert_or_assign("type", "path");
input.attrs.insert_or_assign("path", url.path); input.attrs.insert_or_assign("path", url.path);
@ -54,11 +56,13 @@ struct PathInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const override
{ {
getStrAttr(attrs, "path"); getStrAttr(attrs, "path");
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
return input; return input;
} }

View File

@ -1,7 +1,7 @@
#include "fetch-settings.hh"
#include "registry.hh" #include "registry.hh"
#include "tarball.hh" #include "tarball.hh"
#include "users.hh" #include "users.hh"
#include "config-global.hh"
#include "globals.hh" #include "globals.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
@ -11,12 +11,13 @@
namespace nix::fetchers { namespace nix::fetchers {
std::shared_ptr<Registry> Registry::read( std::shared_ptr<Registry> Registry::read(
const Settings & settings,
const Path & path, RegistryType type) const Path & path, RegistryType type)
{ {
auto registry = std::make_shared<Registry>(type); auto registry = std::make_shared<Registry>(settings, type);
if (!pathExists(path)) if (!pathExists(path))
return std::make_shared<Registry>(type); return std::make_shared<Registry>(settings, type);
try { try {
@ -36,8 +37,8 @@ std::shared_ptr<Registry> Registry::read(
auto exact = i.find("exact"); auto exact = i.find("exact");
registry->entries.push_back( registry->entries.push_back(
Entry { Entry {
.from = Input::fromAttrs(jsonToAttrs(i["from"])), .from = Input::fromAttrs(settings, jsonToAttrs(i["from"])),
.to = Input::fromAttrs(std::move(toAttrs)), .to = Input::fromAttrs(settings, std::move(toAttrs)),
.extraAttrs = extraAttrs, .extraAttrs = extraAttrs,
.exact = exact != i.end() && exact.value() .exact = exact != i.end() && exact.value()
}); });
@ -106,10 +107,10 @@ static Path getSystemRegistryPath()
return settings.nixConfDir + "/registry.json"; return settings.nixConfDir + "/registry.json";
} }
static std::shared_ptr<Registry> getSystemRegistry() static std::shared_ptr<Registry> getSystemRegistry(const Settings & settings)
{ {
static auto systemRegistry = static auto systemRegistry =
Registry::read(getSystemRegistryPath(), Registry::System); Registry::read(settings, getSystemRegistryPath(), Registry::System);
return systemRegistry; return systemRegistry;
} }
@ -118,25 +119,24 @@ Path getUserRegistryPath()
return getConfigDir() + "/nix/registry.json"; return getConfigDir() + "/nix/registry.json";
} }
std::shared_ptr<Registry> getUserRegistry() std::shared_ptr<Registry> getUserRegistry(const Settings & settings)
{ {
static auto userRegistry = static auto userRegistry =
Registry::read(getUserRegistryPath(), Registry::User); Registry::read(settings, getUserRegistryPath(), Registry::User);
return userRegistry; return userRegistry;
} }
std::shared_ptr<Registry> getCustomRegistry(const Path & p) std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Path & p)
{ {
static auto customRegistry = static auto customRegistry =
Registry::read(p, Registry::Custom); Registry::read(settings, p, Registry::Custom);
return customRegistry; return customRegistry;
} }
static std::shared_ptr<Registry> flagRegistry = std::shared_ptr<Registry> getFlagRegistry(const Settings & settings)
std::make_shared<Registry>(Registry::Flag);
std::shared_ptr<Registry> getFlagRegistry()
{ {
static auto flagRegistry =
std::make_shared<Registry>(settings, Registry::Flag);
return flagRegistry; return flagRegistry;
} }
@ -145,30 +145,15 @@ void overrideRegistry(
const Input & to, const Input & to,
const Attrs & extraAttrs) const Attrs & extraAttrs)
{ {
flagRegistry->add(from, to, extraAttrs); getFlagRegistry(*from.settings)->add(from, to, extraAttrs);
} }
struct RegistrySettings : Config static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, ref<Store> store)
{
Setting<std::string> flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry",
R"(
Path or URI of the global flake registry.
When empty, disables the global flake registry.
)",
{}, true, Xp::Flakes};
};
RegistrySettings registrySettings;
static GlobalConfig::Register rRegistrySettings(&registrySettings);
static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
{ {
static auto reg = [&]() { static auto reg = [&]() {
auto path = registrySettings.flakeRegistry.get(); auto path = settings.flakeRegistry.get();
if (path == "") { if (path == "") {
return std::make_shared<Registry>(Registry::Global); // empty registry return std::make_shared<Registry>(settings, Registry::Global); // empty registry
} }
if (!hasPrefix(path, "/")) { if (!hasPrefix(path, "/")) {
@ -178,19 +163,19 @@ static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
path = store->toRealPath(storePath); path = store->toRealPath(storePath);
} }
return Registry::read(path, Registry::Global); return Registry::read(settings, path, Registry::Global);
}(); }();
return reg; return reg;
} }
Registries getRegistries(ref<Store> store) Registries getRegistries(const Settings & settings, ref<Store> store)
{ {
Registries registries; Registries registries;
registries.push_back(getFlagRegistry()); registries.push_back(getFlagRegistry(settings));
registries.push_back(getUserRegistry()); registries.push_back(getUserRegistry(settings));
registries.push_back(getSystemRegistry()); registries.push_back(getSystemRegistry(settings));
registries.push_back(getGlobalRegistry(store)); registries.push_back(getGlobalRegistry(settings, store));
return registries; return registries;
} }
@ -207,7 +192,7 @@ std::pair<Input, Attrs> lookupInRegistries(
n++; n++;
if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string());
for (auto & registry : getRegistries(store)) { for (auto & registry : getRegistries(*input.settings, store)) {
// FIXME: O(n) // FIXME: O(n)
for (auto & entry : registry->entries) { for (auto & entry : registry->entries) {
if (entry.exact) { if (entry.exact) {

View File

@ -10,6 +10,8 @@ namespace nix::fetchers {
struct Registry struct Registry
{ {
const Settings & settings;
enum RegistryType { enum RegistryType {
Flag = 0, Flag = 0,
User = 1, User = 1,
@ -29,11 +31,13 @@ struct Registry
std::vector<Entry> entries; std::vector<Entry> entries;
Registry(RegistryType type) Registry(const Settings & settings, RegistryType type)
: type(type) : settings{settings}
, type{type}
{ } { }
static std::shared_ptr<Registry> read( static std::shared_ptr<Registry> read(
const Settings & settings,
const Path & path, RegistryType type); const Path & path, RegistryType type);
void write(const Path & path); void write(const Path & path);
@ -48,13 +52,13 @@ struct Registry
typedef std::vector<std::shared_ptr<Registry>> Registries; typedef std::vector<std::shared_ptr<Registry>> Registries;
std::shared_ptr<Registry> getUserRegistry(); std::shared_ptr<Registry> getUserRegistry(const Settings & settings);
std::shared_ptr<Registry> getCustomRegistry(const Path & p); std::shared_ptr<Registry> getCustomRegistry(const Settings & settings, const Path & p);
Path getUserRegistryPath(); Path getUserRegistryPath();
Registries getRegistries(ref<Store> store); Registries getRegistries(const Settings & settings, ref<Store> store);
void overrideRegistry( void overrideRegistry(
const Input & from, const Input & from,

View File

@ -214,12 +214,14 @@ struct CurlInputScheme : InputScheme
static const std::set<std::string> specialParams; static const std::set<std::string> specialParams;
std::optional<Input> inputFromURL(const ParsedURL & _url, bool requireTree) const override std::optional<Input> inputFromURL(
const Settings & settings,
const ParsedURL & _url, bool requireTree) const override
{ {
if (!isValidURL(_url, requireTree)) if (!isValidURL(_url, requireTree))
return std::nullopt; return std::nullopt;
Input input; Input input{settings};
auto url = _url; auto url = _url;
@ -267,9 +269,11 @@ struct CurlInputScheme : InputScheme
}; };
} }
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override std::optional<Input> inputFromAttrs(
const Settings & settings,
const Attrs & attrs) const override
{ {
Input input; Input input{settings};
input.attrs = attrs; input.attrs = attrs;
//input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); //input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
@ -349,7 +353,7 @@ struct TarballInputScheme : CurlInputScheme
result.accessor->setPathDisplay("«" + input.to_string() + "»"); result.accessor->setPathDisplay("«" + input.to_string() + "»");
if (result.immutableUrl) { if (result.immutableUrl) {
auto immutableInput = Input::fromURL(*result.immutableUrl); auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl);
// FIXME: would be nice to support arbitrary flakerefs // FIXME: would be nice to support arbitrary flakerefs
// here, e.g. git flakes. // here, e.g. git flakes.
if (immutableInput.getType() != "tarball") if (immutableInput.getType() != "tarball")

View File

@ -1,12 +0,0 @@
#include "flake-settings.hh"
#include "config-global.hh"
namespace nix {
FlakeSettings::FlakeSettings() {}
FlakeSettings flakeSettings;
static GlobalConfig::Register rFlakeSettings(&flakeSettings);
}

View File

@ -1,6 +1,6 @@
#include "users.hh" #include "users.hh"
#include "config-global.hh" #include "config-global.hh"
#include "flake-settings.hh" #include "flake/settings.hh"
#include "flake.hh" #include "flake.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -30,7 +30,7 @@ static void writeTrustedList(const TrustedList & trustedList)
writeFile(path, nlohmann::json(trustedList).dump()); writeFile(path, nlohmann::json(trustedList).dump());
} }
void ConfigFile::apply() void ConfigFile::apply(const Settings & flakeSettings)
{ {
std::set<std::string> whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"}; std::set<std::string> whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"};
@ -51,7 +51,7 @@ void ConfigFile::apply()
else else
assert(false); assert(false);
if (!whitelist.count(baseName) && !nix::flakeSettings.acceptFlakeConfig) { if (!whitelist.count(baseName) && !flakeSettings.acceptFlakeConfig) {
bool trusted = false; bool trusted = false;
auto trustedList = readTrustedList(); auto trustedList = readTrustedList();
auto tlname = get(trustedList, name); auto tlname = get(trustedList, name);

View File

@ -9,7 +9,7 @@
#include "fetchers.hh" #include "fetchers.hh"
#include "finally.hh" #include "finally.hh"
#include "fetch-settings.hh" #include "fetch-settings.hh"
#include "flake-settings.hh" #include "flake/settings.hh"
#include "value-to-json.hh" #include "value-to-json.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
@ -164,7 +164,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
if (attrs.count("type")) if (attrs.count("type"))
try { try {
input.ref = FlakeRef::fromAttrs(attrs); input.ref = FlakeRef::fromAttrs(state.fetchSettings, attrs);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[pos], HintFmt("while evaluating flake input")); e.addTrace(state.positions[pos], HintFmt("while evaluating flake input"));
throw; throw;
@ -174,11 +174,11 @@ static FlakeInput parseFlakeInput(EvalState & state,
if (!attrs.empty()) if (!attrs.empty())
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]); throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
if (url) if (url)
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); input.ref = parseFlakeRef(state.fetchSettings, *url, baseDir, true, input.isFlake);
} }
if (!input.follows && !input.ref) if (!input.follows && !input.ref)
input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", std::string(inputName)}}); input.ref = FlakeRef::fromAttrs(state.fetchSettings, {{"type", "indirect"}, {"id", std::string(inputName)}});
return input; return input;
} }
@ -244,7 +244,7 @@ static Flake readFlake(
for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) {
if (formal.name != state.sSelf) if (formal.name != state.sSelf)
flake.inputs.emplace(state.symbols[formal.name], FlakeInput { flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
.ref = parseFlakeRef(std::string(state.symbols[formal.name])) .ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name]))
}); });
} }
} }
@ -329,16 +329,19 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup
return getFlake(state, originalRef, allowLookup, flakeCache); return getFlake(state, originalRef, allowLookup, flakeCache);
} }
static LockFile readLockFile(const SourcePath & lockFilePath) static LockFile readLockFile(
const fetchers::Settings & fetchSettings,
const SourcePath & lockFilePath)
{ {
return lockFilePath.pathExists() return lockFilePath.pathExists()
? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath)) ? LockFile(fetchSettings, lockFilePath.readFile(), fmt("%s", lockFilePath))
: LockFile(); : LockFile();
} }
/* Compute an in-memory lock file for the specified top-level flake, /* Compute an in-memory lock file for the specified top-level flake,
and optionally write it to file, if the flake is writable. */ and optionally write it to file, if the flake is writable. */
LockedFlake lockFlake( LockedFlake lockFlake(
const Settings & settings,
EvalState & state, EvalState & state,
const FlakeRef & topRef, const FlakeRef & topRef,
const LockFlags & lockFlags) const LockFlags & lockFlags)
@ -347,21 +350,22 @@ LockedFlake lockFlake(
FlakeCache flakeCache; FlakeCache flakeCache;
auto useRegistries = lockFlags.useRegistries.value_or(flakeSettings.useRegistries); auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
auto flake = getFlake(state, topRef, useRegistries, flakeCache); auto flake = getFlake(state, topRef, useRegistries, flakeCache);
if (lockFlags.applyNixConfig) { if (lockFlags.applyNixConfig) {
flake.config.apply(); flake.config.apply(settings);
state.store->setOptions(); state.store->setOptions();
} }
try { try {
if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { if (!state.fetchSettings.allowDirty && lockFlags.referenceLockFilePath) {
throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false"); throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false");
} }
auto oldLockFile = readLockFile( auto oldLockFile = readLockFile(
state.fetchSettings,
lockFlags.referenceLockFilePath.value_or( lockFlags.referenceLockFilePath.value_or(
flake.lockFilePath())); flake.lockFilePath()));
@ -597,7 +601,7 @@ LockedFlake lockFlake(
inputFlake.inputs, childNode, inputPath, inputFlake.inputs, childNode, inputPath,
oldLock oldLock
? std::dynamic_pointer_cast<const Node>(oldLock) ? std::dynamic_pointer_cast<const Node>(oldLock)
: readLockFile(inputFlake.lockFilePath()).root.get_ptr(), : readLockFile(state.fetchSettings, inputFlake.lockFilePath()).root.get_ptr(),
oldLock ? lockRootPath : inputPath, oldLock ? lockRootPath : inputPath,
localPath, localPath,
false); false);
@ -660,7 +664,7 @@ LockedFlake lockFlake(
if (lockFlags.writeLockFile) { if (lockFlags.writeLockFile) {
if (sourcePath || lockFlags.outputLockFilePath) { if (sourcePath || lockFlags.outputLockFilePath) {
if (auto unlockedInput = newLockFile.isUnlocked()) { if (auto unlockedInput = newLockFile.isUnlocked()) {
if (fetchSettings.warnDirty) if (state.fetchSettings.warnDirty)
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput); warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
} else { } else {
if (!lockFlags.updateLockFile) if (!lockFlags.updateLockFile)
@ -692,7 +696,7 @@ LockedFlake lockFlake(
if (lockFlags.commitLockFile) { if (lockFlags.commitLockFile) {
std::string cm; std::string cm;
cm = flakeSettings.commitLockFileSummary.get(); cm = settings.commitLockFileSummary.get();
if (cm == "") { if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
@ -800,46 +804,49 @@ void callFlake(EvalState & state,
state.callFunction(*vTmp1, vOverrides, vRes, noPos); state.callFunction(*vTmp1, vOverrides, vRes, noPos);
} }
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v) void initLib(const Settings & settings)
{ {
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value * * args, Value & v)
auto flakeRef = parseFlakeRef(flakeRefS, {}, true); {
if (state.settings.pureEval && !flakeRef.input.isLocked()) std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); auto flakeRef = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
if (state.settings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
callFlake(state, callFlake(state,
lockFlake(state, flakeRef, lockFlake(settings, state, flakeRef,
LockFlags { LockFlags {
.updateLockFile = false, .updateLockFile = false,
.writeLockFile = false, .writeLockFile = false,
.useRegistries = !state.settings.pureEval && flakeSettings.useRegistries, .useRegistries = !state.settings.pureEval && settings.useRegistries,
.allowUnlocked = !state.settings.pureEval, .allowUnlocked = !state.settings.pureEval,
}), }),
v); v);
};
RegisterPrimOp::primOps->push_back({
.name = "__getFlake",
.args = {"args"},
.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
```
)",
.fun = prim_getFlake,
.experimentalFeature = Xp::Flakes,
});
} }
static RegisterPrimOp r2({
.name = "__getFlake",
.args = {"args"},
.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
```
)",
.fun = prim_getFlake,
.experimentalFeature = Xp::Flakes,
});
static void prim_parseFlakeRef( static void prim_parseFlakeRef(
EvalState & state, EvalState & state,
const PosIdx pos, const PosIdx pos,
@ -848,7 +855,7 @@ static void prim_parseFlakeRef(
{ {
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, std::string flakeRefS(state.forceStringNoCtx(*args[0], pos,
"while evaluating the argument passed to builtins.parseFlakeRef")); "while evaluating the argument passed to builtins.parseFlakeRef"));
auto attrs = parseFlakeRef(flakeRefS, {}, true).toAttrs(); auto attrs = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true).toAttrs();
auto binds = state.buildBindings(attrs.size()); auto binds = state.buildBindings(attrs.size());
for (const auto & [key, value] : attrs) { for (const auto & [key, value] : attrs) {
auto s = state.symbols.create(key); auto s = state.symbols.create(key);
@ -913,7 +920,7 @@ static void prim_flakeRefToString(
showType(*attr.value)).debugThrow(); showType(*attr.value)).debugThrow();
} }
} }
auto flakeRef = FlakeRef::fromAttrs(attrs); auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs);
v.mkString(flakeRef.to_string()); v.mkString(flakeRef.to_string());
} }

View File

@ -12,6 +12,16 @@ class EvalState;
namespace flake { namespace flake {
struct Settings;
/**
* Initialize `libnixflake`
*
* So far, this registers the `builtins.getFlake` primop, which depends
* on the choice of `flake:Settings`.
*/
void initLib(const Settings & settings);
struct FlakeInput; struct FlakeInput;
typedef std::map<FlakeId, FlakeInput> FlakeInputs; typedef std::map<FlakeId, FlakeInput> FlakeInputs;
@ -57,7 +67,7 @@ struct ConfigFile
std::map<std::string, ConfigValue> settings; std::map<std::string, ConfigValue> settings;
void apply(); void apply(const Settings & settings);
}; };
/** /**
@ -194,6 +204,7 @@ struct LockFlags
}; };
LockedFlake lockFlake( LockedFlake lockFlake(
const Settings & settings,
EvalState & state, EvalState & state,
const FlakeRef & flakeRef, const FlakeRef & flakeRef,
const LockFlags & lockFlags); const LockFlags & lockFlags);

View File

@ -48,28 +48,32 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
} }
FlakeRef parseFlakeRef( FlakeRef parseFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir, const std::optional<Path> & baseDir,
bool allowMissing, bool allowMissing,
bool isFlake) bool isFlake)
{ {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
if (fragment != "") if (fragment != "")
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
return flakeRef; return flakeRef;
} }
std::optional<FlakeRef> maybeParseFlakeRef( std::optional<FlakeRef> maybeParseFlakeRef(
const std::string & url, const std::optional<Path> & baseDir) const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<Path> & baseDir)
{ {
try { try {
return parseFlakeRef(url, baseDir); return parseFlakeRef(fetchSettings, url, baseDir);
} catch (Error &) { } catch (Error &) {
return {}; return {};
} }
} }
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment( std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir, const std::optional<Path> & baseDir,
bool allowMissing, bool allowMissing,
@ -166,7 +170,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
parsedURL.query.insert_or_assign("shallow", "1"); parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair( return std::make_pair(
FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")), FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL), getOr(parsedURL.query, "dir", "")),
fragment); fragment);
} }
@ -185,13 +189,14 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
attrs.insert_or_assign("type", "path"); attrs.insert_or_assign("type", "path");
attrs.insert_or_assign("path", path); attrs.insert_or_assign("path", path);
return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment); return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment);
}; };
/* Check if 'url' is a flake ID. This is an abbreviated syntax for /* Check if 'url' is a flake ID. This is an abbreviated syntax for
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */ 'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef( static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
bool isFlake bool isFlake
) )
@ -213,7 +218,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
}; };
return std::make_pair( return std::make_pair(
FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""), FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake), ""),
percentDecode(match.str(6))); percentDecode(match.str(6)));
} }
@ -221,6 +226,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
} }
std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef( std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir, const std::optional<Path> & baseDir,
bool isFlake bool isFlake
@ -236,7 +242,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
std::string fragment; std::string fragment;
std::swap(fragment, parsedURL.fragment); std::swap(fragment, parsedURL.fragment);
auto input = fetchers::Input::fromURL(parsedURL, isFlake); auto input = fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake);
input.parent = baseDir; input.parent = baseDir;
return std::make_pair( return std::make_pair(
@ -245,6 +251,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
} }
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir, const std::optional<Path> & baseDir,
bool allowMissing, bool allowMissing,
@ -254,31 +261,34 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
std::smatch match; std::smatch match;
if (auto res = parseFlakeIdRef(url, isFlake)) { if (auto res = parseFlakeIdRef(fetchSettings, url, isFlake)) {
return *res; return *res;
} else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) { } else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) {
return *res; return *res;
} else { } else {
return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
} }
} }
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment( std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::optional<Path> & baseDir) const std::string & url, const std::optional<Path> & baseDir)
{ {
try { try {
return parseFlakeRefWithFragment(url, baseDir); return parseFlakeRefWithFragment(fetchSettings, url, baseDir);
} catch (Error & e) { } catch (Error & e) {
return {}; return {};
} }
} }
FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) FlakeRef FlakeRef::fromAttrs(
const fetchers::Settings & fetchSettings,
const fetchers::Attrs & attrs)
{ {
auto attrs2(attrs); auto attrs2(attrs);
attrs2.erase("dir"); attrs2.erase("dir");
return FlakeRef( return FlakeRef(
fetchers::Input::fromAttrs(std::move(attrs2)), fetchers::Input::fromAttrs(fetchSettings, std::move(attrs2)),
fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); fetchers::maybeGetStrAttr(attrs, "dir").value_or(""));
} }
@ -289,13 +299,16 @@ std::pair<StorePath, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
} }
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec( std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir, const std::optional<Path> & baseDir,
bool allowMissing, bool allowMissing,
bool isFlake) bool isFlake)
{ {
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url); auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url);
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, baseDir, allowMissing, isFlake); auto [flakeRef, fragment] = parseFlakeRefWithFragment(
fetchSettings,
std::string { prefix }, baseDir, allowMissing, isFlake);
return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)}; return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)};
} }

View File

@ -61,7 +61,9 @@ struct FlakeRef
FlakeRef resolve(ref<Store> store) const; FlakeRef resolve(ref<Store> store) const;
static FlakeRef fromAttrs(const fetchers::Attrs & attrs); static FlakeRef fromAttrs(
const fetchers::Settings & fetchSettings,
const fetchers::Attrs & attrs);
std::pair<StorePath, FlakeRef> fetchTree(ref<Store> store) const; std::pair<StorePath, FlakeRef> fetchTree(ref<Store> store) const;
}; };
@ -72,6 +74,7 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
*/ */
FlakeRef parseFlakeRef( FlakeRef parseFlakeRef(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir = {}, const std::optional<Path> & baseDir = {},
bool allowMissing = false, bool allowMissing = false,
@ -81,12 +84,15 @@ FlakeRef parseFlakeRef(
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
*/ */
std::optional<FlakeRef> maybeParseFlake( std::optional<FlakeRef> maybeParseFlake(
const std::string & url, const std::optional<Path> & baseDir = {}); const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<Path> & baseDir = {});
/** /**
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
*/ */
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir = {}, const std::optional<Path> & baseDir = {},
bool allowMissing = false, bool allowMissing = false,
@ -96,12 +102,15 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
*/ */
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment( std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir = {}); const fetchers::Settings & fetchSettings,
const std::string & url,
const std::optional<Path> & baseDir = {});
/** /**
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
*/ */
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec( std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
const std::optional<Path> & baseDir = {}, const std::optional<Path> & baseDir = {},
bool allowMissing = false, bool allowMissing = false,

View File

@ -10,7 +10,8 @@
namespace nix::flake { namespace nix::flake {
FlakeRef getFlakeRef( static FlakeRef getFlakeRef(
const fetchers::Settings & fetchSettings,
const nlohmann::json & json, const nlohmann::json & json,
const char * attr, const char * attr,
const char * info) const char * info)
@ -26,15 +27,17 @@ FlakeRef getFlakeRef(
attrs.insert_or_assign(k.first, k.second); attrs.insert_or_assign(k.first, k.second);
} }
} }
return FlakeRef::fromAttrs(attrs); return FlakeRef::fromAttrs(fetchSettings, attrs);
} }
throw Error("attribute '%s' missing in lock file", attr); throw Error("attribute '%s' missing in lock file", attr);
} }
LockedNode::LockedNode(const nlohmann::json & json) LockedNode::LockedNode(
: lockedRef(getFlakeRef(json, "locked", "info")) // FIXME: remove "info" const fetchers::Settings & fetchSettings,
, originalRef(getFlakeRef(json, "original", nullptr)) const nlohmann::json & json)
: lockedRef(getFlakeRef(fetchSettings, json, "locked", "info")) // FIXME: remove "info"
, originalRef(getFlakeRef(fetchSettings, json, "original", nullptr))
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{ {
if (!lockedRef.input.isLocked()) if (!lockedRef.input.isLocked())
@ -84,7 +87,9 @@ std::shared_ptr<Node> LockFile::findInput(const InputPath & path)
return doFind(root, path, visited); return doFind(root, path, visited);
} }
LockFile::LockFile(std::string_view contents, std::string_view path) LockFile::LockFile(
const fetchers::Settings & fetchSettings,
std::string_view contents, std::string_view path)
{ {
auto json = nlohmann::json::parse(contents); auto json = nlohmann::json::parse(contents);
@ -113,7 +118,7 @@ LockFile::LockFile(std::string_view contents, std::string_view path)
auto jsonNode2 = nodes.find(inputKey); auto jsonNode2 = nodes.find(inputKey);
if (jsonNode2 == nodes.end()) if (jsonNode2 == nodes.end())
throw Error("lock file references missing node '%s'", inputKey); throw Error("lock file references missing node '%s'", inputKey);
auto input = make_ref<LockedNode>(*jsonNode2); auto input = make_ref<LockedNode>(fetchSettings, *jsonNode2);
k = nodeMap.insert_or_assign(inputKey, input).first; k = nodeMap.insert_or_assign(inputKey, input).first;
getInputs(*input, *jsonNode2); getInputs(*input, *jsonNode2);
} }

View File

@ -45,7 +45,9 @@ struct LockedNode : Node
: lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake) : lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake)
{ } { }
LockedNode(const nlohmann::json & json); LockedNode(
const fetchers::Settings & fetchSettings,
const nlohmann::json & json);
StorePath computeStorePath(Store & store) const; StorePath computeStorePath(Store & store) const;
}; };
@ -55,7 +57,9 @@ struct LockFile
ref<Node> root = make_ref<Node>(); ref<Node> root = make_ref<Node>();
LockFile() {}; LockFile() {};
LockFile(std::string_view contents, std::string_view path); LockFile(
const fetchers::Settings & fetchSettings,
std::string_view contents, std::string_view path);
typedef std::map<ref<const Node>, std::string> KeyMap; typedef std::map<ref<const Node>, std::string> KeyMap;

View File

@ -0,0 +1,7 @@
#include "flake/settings.hh"
namespace nix::flake {
Settings::Settings() {}
}

View File

@ -10,11 +10,11 @@
#include <sys/types.h> #include <sys/types.h>
namespace nix { namespace nix::flake {
struct FlakeSettings : public Config struct Settings : public Config
{ {
FlakeSettings(); Settings();
Setting<bool> useRegistries{ Setting<bool> useRegistries{
this, this,
@ -47,7 +47,4 @@ struct FlakeSettings : public Config
Xp::Flakes}; Xp::Flakes};
}; };
// TODO: don't use a global variable.
extern FlakeSettings flakeSettings;
} }

View File

@ -42,21 +42,21 @@ add_project_arguments(
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/diagnostics')
sources = files( sources = files(
'flake-settings.cc',
'flake/config.cc', 'flake/config.cc',
'flake/flake.cc', 'flake/flake.cc',
'flake/flakeref.cc', 'flake/flakeref.cc',
'flake/url-name.cc',
'flake/lockfile.cc', 'flake/lockfile.cc',
'flake/settings.cc',
'flake/url-name.cc',
) )
include_dirs = [include_directories('.')] include_dirs = [include_directories('.')]
headers = files( headers = files(
'flake-settings.hh',
'flake/flake.hh', 'flake/flake.hh',
'flake/flakeref.hh', 'flake/flakeref.hh',
'flake/lockfile.hh', 'flake/lockfile.hh',
'flake/settings.hh',
'flake/url-name.hh', 'flake/url-name.hh',
) )

View File

@ -286,7 +286,7 @@ static void main_nix_build(int argc, char * * argv)
auto store = openStore(); auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto state = std::make_unique<EvalState>(myArgs.lookupPath, evalStore, evalSettings, store); auto state = std::make_unique<EvalState>(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store);
state->repair = myArgs.repair; state->repair = myArgs.repair;
if (myArgs.repair) buildMode = bmRepair; if (myArgs.repair) buildMode = bmRepair;

View File

@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv)
auto store = openStore(); auto store = openStore();
globals.state = std::shared_ptr<EvalState>(new EvalState(myArgs.lookupPath, store, evalSettings)); globals.state = std::shared_ptr<EvalState>(new EvalState(myArgs.lookupPath, store, fetchSettings, evalSettings));
globals.state->repair = myArgs.repair; globals.state->repair = myArgs.repair;
globals.instSource.nixExprPath = std::make_shared<SourcePath>( globals.instSource.nixExprPath = std::make_shared<SourcePath>(

View File

@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv)
auto store = openStore(); auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto state = std::make_unique<EvalState>(myArgs.lookupPath, evalStore, evalSettings, store); auto state = std::make_unique<EvalState>(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store);
state->repair = myArgs.repair; state->repair = myArgs.repair;
Bindings & autoArgs = *myArgs.getAutoArgs(*state); Bindings & autoArgs = *myArgs.getAutoArgs(*state);

View File

@ -76,7 +76,9 @@ struct CmdBundle : InstallableValueCommand
auto val = installable->toValue(*evalState).first; auto val = installable->toValue(*evalState).first;
auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = parseFlakeRefWithFragmentAndExtendedOutputsSpec(bundler, absPath(".")); auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] =
parseFlakeRefWithFragmentAndExtendedOutputsSpec(
fetchSettings, bundler, absPath("."));
const flake::LockFlags lockFlags{ .writeLockFile = false }; const flake::LockFlags lockFlags{ .writeLockFile = false };
InstallableFlake bundler{this, InstallableFlake bundler{this,
evalState, std::move(bundlerFlakeRef), bundlerName, std::move(extendedOutputsSpec), evalState, std::move(bundlerFlakeRef), bundlerName, std::move(extendedOutputsSpec),

View File

@ -48,19 +48,19 @@ public:
FlakeRef getFlakeRef() FlakeRef getFlakeRef()
{ {
return parseFlakeRef(flakeUrl, absPath(".")); //FIXME return parseFlakeRef(fetchSettings, flakeUrl, absPath(".")); //FIXME
} }
LockedFlake lockFlake() LockedFlake lockFlake()
{ {
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); return flake::lockFlake(flakeSettings, *getEvalState(), getFlakeRef(), lockFlags);
} }
std::vector<FlakeRef> getFlakeRefsForCompletion() override std::vector<FlakeRef> getFlakeRefsForCompletion() override
{ {
return { return {
// Like getFlakeRef but with expandTilde calld first // Like getFlakeRef but with expandTilde calld first
parseFlakeRef(expandTilde(flakeUrl), absPath(".")) parseFlakeRef(fetchSettings, expandTilde(flakeUrl), absPath("."))
}; };
} }
}; };
@ -848,7 +848,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
auto evalState = getEvalState(); auto evalState = getEvalState();
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(
fetchSettings, templateUrl, absPath("."));
auto installable = InstallableFlake(nullptr, auto installable = InstallableFlake(nullptr,
evalState, std::move(templateFlakeRef), templateName, ExtendedOutputsSpec::Default(), evalState, std::move(templateFlakeRef), templateName, ExtendedOutputsSpec::Default(),

View File

@ -19,6 +19,7 @@
#include "users.hh" #include "users.hh"
#include "network-proxy.hh" #include "network-proxy.hh"
#include "eval-cache.hh" #include "eval-cache.hh"
#include "flake/flake.hh"
#include <sys/types.h> #include <sys/types.h>
#include <regex> #include <regex>
@ -242,7 +243,7 @@ static void showHelp(std::vector<std::string> subcommand, NixArgs & toplevel)
evalSettings.restrictEval = false; evalSettings.restrictEval = false;
evalSettings.pureEval = false; evalSettings.pureEval = false;
EvalState state({}, openStore("dummy://"), evalSettings); EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings);
auto vGenerateManpage = state.allocValue(); auto vGenerateManpage = state.allocValue();
state.eval(state.parseExprFromString( state.eval(state.parseExprFromString(
@ -362,6 +363,7 @@ void mainWrapped(int argc, char * * argv)
initNix(); initNix();
initGC(); initGC();
flake::initLib(flakeSettings);
#if __linux__ #if __linux__
if (isRootUser()) { if (isRootUser()) {
@ -418,7 +420,7 @@ void mainWrapped(int argc, char * * argv)
Xp::FetchTree, Xp::FetchTree,
}; };
evalSettings.pureEval = false; evalSettings.pureEval = false;
EvalState state({}, openStore("dummy://"), evalSettings); EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings);
auto builtinsJson = nlohmann::json::object(); auto builtinsJson = nlohmann::json::object();
for (auto & builtin : *state.baseEnv.values[0]->attrs()) { for (auto & builtin : *state.baseEnv.values[0]->attrs()) {
auto b = nlohmann::json::object(); auto b = nlohmann::json::object();

View File

@ -195,7 +195,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
startProgressBar(); startProgressBar();
auto store = openStore(); auto store = openStore();
auto state = std::make_unique<EvalState>(myArgs.lookupPath, store, evalSettings); auto state = std::make_unique<EvalState>(myArgs.lookupPath, store, fetchSettings, evalSettings);
Bindings & autoArgs = *myArgs.getAutoArgs(*state); Bindings & autoArgs = *myArgs.getAutoArgs(*state);

View File

@ -154,8 +154,8 @@ struct ProfileManifest
} }
if (e.value(sUrl, "") != "") { if (e.value(sUrl, "") != "") {
element.source = ProfileElementSource { element.source = ProfileElementSource {
parseFlakeRef(e[sOriginalUrl]), parseFlakeRef(fetchSettings, e[sOriginalUrl]),
parseFlakeRef(e[sUrl]), parseFlakeRef(fetchSettings, e[sUrl]),
e["attrPath"], e["attrPath"],
e["outputs"].get<ExtendedOutputsSpec>() e["outputs"].get<ExtendedOutputsSpec>()
}; };

View File

@ -33,9 +33,9 @@ public:
{ {
if (registry) return registry; if (registry) return registry;
if (registry_path.empty()) { if (registry_path.empty()) {
registry = fetchers::getUserRegistry(); registry = fetchers::getUserRegistry(fetchSettings);
} else { } else {
registry = fetchers::getCustomRegistry(registry_path); registry = fetchers::getCustomRegistry(fetchSettings, registry_path);
} }
return registry; return registry;
} }
@ -68,7 +68,7 @@ struct CmdRegistryList : StoreCommand
{ {
using namespace fetchers; using namespace fetchers;
auto registries = getRegistries(store); auto registries = getRegistries(fetchSettings, store);
for (auto & registry : registries) { for (auto & registry : registries) {
for (auto & entry : registry->entries) { for (auto & entry : registry->entries) {
@ -109,8 +109,8 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand
void run() override void run() override
{ {
auto fromRef = parseFlakeRef(fromUrl); auto fromRef = parseFlakeRef(fetchSettings, fromUrl);
auto toRef = parseFlakeRef(toUrl); auto toRef = parseFlakeRef(fetchSettings, toUrl);
auto registry = getRegistry(); auto registry = getRegistry();
fetchers::Attrs extraAttrs; fetchers::Attrs extraAttrs;
if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir;
@ -144,7 +144,7 @@ struct CmdRegistryRemove : RegistryCommand, Command
void run() override void run() override
{ {
auto registry = getRegistry(); auto registry = getRegistry();
registry->remove(parseFlakeRef(url).input); registry->remove(parseFlakeRef(fetchSettings, url).input);
registry->write(getRegistryPath()); registry->write(getRegistryPath());
} }
}; };
@ -185,8 +185,8 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand
{ {
if (locked.empty()) locked = url; if (locked.empty()) locked = url;
auto registry = getRegistry(); auto registry = getRegistry();
auto ref = parseFlakeRef(url); auto ref = parseFlakeRef(fetchSettings, url);
auto lockedRef = parseFlakeRef(locked); auto lockedRef = parseFlakeRef(fetchSettings, locked);
registry->remove(ref.input); registry->remove(ref.input);
auto resolved = lockedRef.resolve(store).input.getAccessor(store).second; auto resolved = lockedRef.resolve(store).input.getAccessor(store).second;
if (!resolved.isLocked()) if (!resolved.isLocked())

View File

@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl);
auto res = getFileTransfer()->download(req); auto res = getFileTransfer()->download(req);
auto state = std::make_unique<EvalState>(LookupPath{}, store, evalSettings); auto state = std::make_unique<EvalState>(LookupPath{}, store, fetchSettings, evalSettings);
auto v = state->allocValue(); auto v = state->allocValue();
state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v);
Bindings & bindings(*state->allocBindings(0)); Bindings & bindings(*state->allocBindings(0));

View File

@ -4,8 +4,10 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "fetch-settings.hh"
#include "value.hh" #include "value.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
#include "nixexpr.hh"
#include "eval.hh" #include "eval.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
#include "eval-settings.hh" #include "eval-settings.hh"
@ -24,7 +26,7 @@ namespace nix {
protected: protected:
LibExprTest() LibExprTest()
: LibStoreTest() : LibStoreTest()
, state({}, store, evalSettings, nullptr) , state({}, store, fetchSettings, evalSettings, nullptr)
{ {
evalSettings.nixPath = {}; evalSettings.nixPath = {};
} }
@ -43,6 +45,7 @@ namespace nix {
} }
bool readOnlyMode = true; bool readOnlyMode = true;
fetchers::Settings fetchSettings{};
EvalSettings evalSettings{readOnlyMode}; EvalSettings evalSettings{readOnlyMode};
EvalState state; EvalState state;
}; };

View File

@ -1,5 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "fetch-settings.hh"
#include "flake/flakeref.hh" #include "flake/flakeref.hh"
namespace nix { namespace nix {
@ -11,8 +12,9 @@ namespace nix {
* --------------------------------------------------------------------------*/ * --------------------------------------------------------------------------*/
TEST(to_string, doesntReencodeUrl) { TEST(to_string, doesntReencodeUrl) {
fetchers::Settings fetchSettings;
auto s = "http://localhost:8181/test/+3d.tar.gz"; auto s = "http://localhost:8181/test/+3d.tar.gz";
auto flakeref = parseFlakeRef(s); auto flakeref = parseFlakeRef(fetchSettings, s);
auto parsed = flakeref.to_string(); auto parsed = flakeref.to_string();
auto expected = "http://localhost:8181/test/%2B3d.tar.gz"; auto expected = "http://localhost:8181/test/%2B3d.tar.gz";