Allow nix.conf options to be set in flake.nix
This makes it possible to have per-project configuration in flake.nix, e.g. binary caches and other stuff: nixConfig.bash-prompt-suffix = "[1;35mngi# [0m"; nixConfig.substituters = [ "https://cache.ngi0.nixos.org/" ];
This commit is contained in:
parent
731edf0d9b
commit
343239fc8a
|
@ -71,11 +71,17 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||||
return {std::move(tree), resolvedRef, lockedRef};
|
return {std::move(tree), resolvedRef, lockedRef};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expectType(EvalState & state, ValueType type,
|
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
||||||
Value & value, const Pos & pos)
|
|
||||||
{
|
{
|
||||||
if (value.type == tThunk && value.isTrivial())
|
if (value.type == tThunk && value.isTrivial())
|
||||||
state.forceValue(value, pos);
|
state.forceValue(value, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void expectType(EvalState & state, ValueType type,
|
||||||
|
Value & value, const Pos & pos)
|
||||||
|
{
|
||||||
|
forceTrivialValue(state, value, pos);
|
||||||
if (value.type != type)
|
if (value.type != type)
|
||||||
throw Error("expected %s but got %s at %s",
|
throw Error("expected %s but got %s at %s",
|
||||||
showType(type), showType(value.type), pos);
|
showType(type), showType(value.type), pos);
|
||||||
|
@ -114,7 +120,6 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
expectType(state, tString, *attr.value, *attr.pos);
|
expectType(state, tString, *attr.value, *attr.pos);
|
||||||
input.follows = parseInputPath(attr.value->string.s);
|
input.follows = parseInputPath(attr.value->string.s);
|
||||||
} else {
|
} else {
|
||||||
state.forceValue(*attr.value);
|
|
||||||
if (attr.value->type == tString)
|
if (attr.value->type == tString)
|
||||||
attrs.emplace(attr.name, attr.value->string.s);
|
attrs.emplace(attr.name, attr.value->string.s);
|
||||||
else
|
else
|
||||||
|
@ -223,10 +228,41 @@ static Flake getFlake(
|
||||||
} else
|
} else
|
||||||
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
|
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
|
||||||
|
|
||||||
|
auto sNixConfig = state.symbols.create("nixConfig");
|
||||||
|
|
||||||
|
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||||
|
expectType(state, tAttrs, *nixConfig->value, *nixConfig->pos);
|
||||||
|
|
||||||
|
for (auto & option : *nixConfig->value->attrs) {
|
||||||
|
forceTrivialValue(state, *option.value, *option.pos);
|
||||||
|
if (option.value->type == tString)
|
||||||
|
flake.config.options.insert({option.name, state.forceStringNoCtx(*option.value, *option.pos)});
|
||||||
|
else if (option.value->type == tInt)
|
||||||
|
flake.config.options.insert({option.name, state.forceInt(*option.value, *option.pos)});
|
||||||
|
else if (option.value->type == tBool)
|
||||||
|
flake.config.options.insert({option.name, state.forceBool(*option.value, *option.pos)});
|
||||||
|
else if (option.value->isList()) {
|
||||||
|
std::vector<std::string> ss;
|
||||||
|
for (unsigned int n = 0; n < option.value->listSize(); ++n) {
|
||||||
|
auto elem = option.value->listElems()[n];
|
||||||
|
if (elem->type != tString)
|
||||||
|
throw TypeError("list element in flake configuration option '%s' is %s while a string is expected",
|
||||||
|
option.name, showType(*option.value));
|
||||||
|
ss.push_back(state.forceStringNoCtx(*elem, *option.pos));
|
||||||
|
}
|
||||||
|
flake.config.options.insert({option.name, ss});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw TypeError("flake configuration option '%s' is %s",
|
||||||
|
option.name, showType(*option.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto & attr : *vInfo.attrs) {
|
for (auto & attr : *vInfo.attrs) {
|
||||||
if (attr.name != state.sDescription &&
|
if (attr.name != state.sDescription &&
|
||||||
attr.name != sInputs &&
|
attr.name != sInputs &&
|
||||||
attr.name != sOutputs)
|
attr.name != sOutputs &&
|
||||||
|
attr.name != sNixConfig)
|
||||||
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
||||||
lockedRef, attr.name, *attr.pos);
|
lockedRef, attr.name, *attr.pos);
|
||||||
}
|
}
|
||||||
|
@ -599,4 +635,30 @@ Fingerprint LockedFlake::getFingerprint() const
|
||||||
|
|
||||||
Flake::~Flake() { }
|
Flake::~Flake() { }
|
||||||
|
|
||||||
|
void ConfigFile::apply()
|
||||||
|
{
|
||||||
|
for (auto & [name, value] : options) {
|
||||||
|
// FIXME: support 'trusted-public-keys' (and other options), but make it TOFU.
|
||||||
|
if (name != "bash-prompt-suffix" &&
|
||||||
|
name != "bash-prompt" &&
|
||||||
|
name != "substituters" &&
|
||||||
|
name != "extra-substituters")
|
||||||
|
{
|
||||||
|
warn("ignoring untrusted flake configuration option '%s'", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// FIXME: Move into libutil/config.cc.
|
||||||
|
if (auto s = std::get_if<std::string>(&value))
|
||||||
|
globalConfig.set(name, *s);
|
||||||
|
else if (auto n = std::get_if<int64_t>(&value))
|
||||||
|
globalConfig.set(name, fmt("%d", n));
|
||||||
|
else if (auto b = std::get_if<Explicit<bool>>(&value))
|
||||||
|
globalConfig.set(name, b->t ? "true" : "false");
|
||||||
|
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
|
||||||
|
globalConfig.set(name, concatStringsSep(" ", *ss)); // FIXME: evil
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,16 @@ struct FlakeInput
|
||||||
FlakeInputs overrides;
|
FlakeInputs overrides;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The Flake structure is the main internal representation of a flake.nix file.
|
struct ConfigFile
|
||||||
|
{
|
||||||
|
using ConfigValue = std::variant<std::string, int64_t, Explicit<bool>, std::vector<std::string>>;
|
||||||
|
|
||||||
|
std::map<std::string, ConfigValue> options;
|
||||||
|
|
||||||
|
void apply();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The contents of a flake.nix file. */
|
||||||
struct Flake
|
struct Flake
|
||||||
{
|
{
|
||||||
FlakeRef originalRef; // the original flake specification (by the user)
|
FlakeRef originalRef; // the original flake specification (by the user)
|
||||||
|
@ -57,6 +65,7 @@ struct Flake
|
||||||
std::optional<std::string> description;
|
std::optional<std::string> description;
|
||||||
std::shared_ptr<const fetchers::Tree> sourceInfo;
|
std::shared_ptr<const fetchers::Tree> sourceInfo;
|
||||||
FlakeInputs inputs;
|
FlakeInputs inputs;
|
||||||
|
ConfigFile config; // 'nixConfig' attribute
|
||||||
~Flake();
|
~Flake();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -533,8 +533,11 @@ InstallableFlake::getCursors(EvalState & state)
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
{
|
{
|
||||||
if (!_lockedFlake)
|
if (!_lockedFlake) {
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
|
||||||
|
_lockedFlake->flake.config.apply();
|
||||||
|
// FIXME: send new config to the daemon.
|
||||||
|
}
|
||||||
return _lockedFlake;
|
return _lockedFlake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue