Skip to content

Commit 343239f

Browse files
committed
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/" ];
1 parent 731edf0 commit 343239f

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

src/libexpr/flake/flake.cc

+66-4
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,17 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
7171
return {std::move(tree), resolvedRef, lockedRef};
7272
}
7373

74-
static void expectType(EvalState & state, ValueType type,
75-
Value & value, const Pos & pos)
74+
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
7675
{
7776
if (value.type == tThunk && value.isTrivial())
7877
state.forceValue(value, pos);
78+
}
79+
80+
81+
static void expectType(EvalState & state, ValueType type,
82+
Value & value, const Pos & pos)
83+
{
84+
forceTrivialValue(state, value, pos);
7985
if (value.type != type)
8086
throw Error("expected %s but got %s at %s",
8187
showType(type), showType(value.type), pos);
@@ -114,7 +120,6 @@ static FlakeInput parseFlakeInput(EvalState & state,
114120
expectType(state, tString, *attr.value, *attr.pos);
115121
input.follows = parseInputPath(attr.value->string.s);
116122
} else {
117-
state.forceValue(*attr.value);
118123
if (attr.value->type == tString)
119124
attrs.emplace(attr.name, attr.value->string.s);
120125
else
@@ -223,10 +228,41 @@ static Flake getFlake(
223228
} else
224229
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
225230

231+
auto sNixConfig = state.symbols.create("nixConfig");
232+
233+
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
234+
expectType(state, tAttrs, *nixConfig->value, *nixConfig->pos);
235+
236+
for (auto & option : *nixConfig->value->attrs) {
237+
forceTrivialValue(state, *option.value, *option.pos);
238+
if (option.value->type == tString)
239+
flake.config.options.insert({option.name, state.forceStringNoCtx(*option.value, *option.pos)});
240+
else if (option.value->type == tInt)
241+
flake.config.options.insert({option.name, state.forceInt(*option.value, *option.pos)});
242+
else if (option.value->type == tBool)
243+
flake.config.options.insert({option.name, state.forceBool(*option.value, *option.pos)});
244+
else if (option.value->isList()) {
245+
std::vector<std::string> ss;
246+
for (unsigned int n = 0; n < option.value->listSize(); ++n) {
247+
auto elem = option.value->listElems()[n];
248+
if (elem->type != tString)
249+
throw TypeError("list element in flake configuration option '%s' is %s while a string is expected",
250+
option.name, showType(*option.value));
251+
ss.push_back(state.forceStringNoCtx(*elem, *option.pos));
252+
}
253+
flake.config.options.insert({option.name, ss});
254+
}
255+
else
256+
throw TypeError("flake configuration option '%s' is %s",
257+
option.name, showType(*option.value));
258+
}
259+
}
260+
226261
for (auto & attr : *vInfo.attrs) {
227262
if (attr.name != state.sDescription &&
228263
attr.name != sInputs &&
229-
attr.name != sOutputs)
264+
attr.name != sOutputs &&
265+
attr.name != sNixConfig)
230266
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
231267
lockedRef, attr.name, *attr.pos);
232268
}
@@ -599,4 +635,30 @@ Fingerprint LockedFlake::getFingerprint() const
599635

600636
Flake::~Flake() { }
601637

638+
void ConfigFile::apply()
639+
{
640+
for (auto & [name, value] : options) {
641+
// FIXME: support 'trusted-public-keys' (and other options), but make it TOFU.
642+
if (name != "bash-prompt-suffix" &&
643+
name != "bash-prompt" &&
644+
name != "substituters" &&
645+
name != "extra-substituters")
646+
{
647+
warn("ignoring untrusted flake configuration option '%s'", name);
648+
continue;
649+
}
650+
// FIXME: Move into libutil/config.cc.
651+
if (auto s = std::get_if<std::string>(&value))
652+
globalConfig.set(name, *s);
653+
else if (auto n = std::get_if<int64_t>(&value))
654+
globalConfig.set(name, fmt("%d", n));
655+
else if (auto b = std::get_if<Explicit<bool>>(&value))
656+
globalConfig.set(name, b->t ? "true" : "false");
657+
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
658+
globalConfig.set(name, concatStringsSep(" ", *ss)); // FIXME: evil
659+
else
660+
assert(false);
661+
}
662+
}
663+
602664
}

src/libexpr/flake/flake.hh

+10-1
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,16 @@ struct FlakeInput
4747
FlakeInputs overrides;
4848
};
4949

50-
// The Flake structure is the main internal representation of a flake.nix file.
50+
struct ConfigFile
51+
{
52+
using ConfigValue = std::variant<std::string, int64_t, Explicit<bool>, std::vector<std::string>>;
53+
54+
std::map<std::string, ConfigValue> options;
55+
56+
void apply();
57+
};
5158

59+
/* The contents of a flake.nix file. */
5260
struct Flake
5361
{
5462
FlakeRef originalRef; // the original flake specification (by the user)
@@ -57,6 +65,7 @@ struct Flake
5765
std::optional<std::string> description;
5866
std::shared_ptr<const fetchers::Tree> sourceInfo;
5967
FlakeInputs inputs;
68+
ConfigFile config; // 'nixConfig' attribute
6069
~Flake();
6170
};
6271

src/nix/installables.cc

+4-1
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,11 @@ InstallableFlake::getCursors(EvalState & state)
533533

534534
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
535535
{
536-
if (!_lockedFlake)
536+
if (!_lockedFlake) {
537537
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
538+
_lockedFlake->flake.config.apply();
539+
// FIXME: send new config to the daemon.
540+
}
538541
return _lockedFlake;
539542
}
540543

0 commit comments

Comments
 (0)