nix flake check: Do some basic checks on NixOS modules

Also show more position info.
This commit is contained in:
Eelco Dolstra 2019-09-10 15:25:10 +02:00
parent dc3f52a144
commit 4b9dee6bcc
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE

View file

@ -170,7 +170,7 @@ struct CmdFlakeUpdate : FlakeCommand
}; };
static void enumerateOutputs(EvalState & state, Value & vFlake, static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide)> callback) std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
{ {
state.forceAttrs(vFlake); state.forceAttrs(vFlake);
@ -179,7 +179,7 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
state.forceAttrs(*vOutputs); state.forceAttrs(*vOutputs);
for (auto & attr : *vOutputs->attrs) for (auto & attr : *vOutputs->attrs)
callback(attr.name, *attr.value); callback(attr.name, *attr.value, *attr.pos);
} }
struct CmdFlakeInfo : FlakeCommand, MixJSON struct CmdFlakeInfo : FlakeCommand, MixJSON
@ -207,7 +207,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON
enumerateOutputs(*state, enumerateOutputs(*state,
*vFlake, *vFlake,
[&](const std::string & name, Value & vProvide) { [&](const std::string & name, Value & vProvide, const Pos & pos) {
auto provide = nlohmann::json::object(); auto provide = nlohmann::json::object();
if (name == "checks" || name == "packages") { if (name == "checks" || name == "packages") {
@ -251,7 +251,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
auto state = getEvalState(); auto state = getEvalState();
auto flake = resolveFlake(); auto flake = resolveFlake();
auto checkDerivation = [&](const std::string & attrPath, Value & v) { auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try { try {
auto drvInfo = getDerivation(*state, v, false); auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo) if (!drvInfo)
@ -259,14 +259,14 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
// FIXME: check meta attributes // FIXME: check meta attributes
return drvInfo->queryDrvPath(); return drvInfo->queryDrvPath();
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
throw; throw;
} }
}; };
PathSet drvPaths; PathSet drvPaths;
auto checkApp = [&](const std::string & attrPath, Value & v) { auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try { try {
auto app = App(*state, v); auto app = App(*state, v);
for (auto & i : app.context) { for (auto & i : app.context) {
@ -275,12 +275,12 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
drvPaths.insert(drvPath + "!" + outputName); drvPaths.insert(drvPath + "!" + outputName);
} }
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
throw; throw;
} }
}; };
auto checkOverlay = [&](const std::string & attrPath, Value & v) { auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try { try {
state->forceValue(v); state->forceValue(v);
if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final") if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final")
@ -291,7 +291,31 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
// FIXME: if we have a 'nixpkgs' input, use it to // FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay. // evaluate the overlay.
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
throw;
}
};
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v);
if (v.type == tLambda) {
if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis)
throw Error("module must match an open attribute set ('{ config, ... }')");
} else if (v.type == tAttrs) {
for (auto & attr : *v.attrs)
try {
state->forceValue(*attr.value);
} catch (Error & e) {
e.addPrefix(fmt("while evaluating the option '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attr.name, *attr.pos));
throw;
}
} else
throw Error("module must be a function or an attribute set");
// FIXME: if we have a 'nixpkgs' input, use it to
// check the module.
} catch (Error & e) {
e.addPrefix(fmt("while checking the NixOS module '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
throw; throw;
} }
}; };
@ -304,46 +328,63 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
enumerateOutputs(*state, enumerateOutputs(*state,
*vFlake, *vFlake,
[&](const std::string & name, Value & vProvide) { [&](const std::string & name, Value & vOutput, const Pos & pos) {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking flake output '%s'", name)); fmt("checking flake output '%s'", name));
try { try {
state->forceValue(vProvide); state->forceValue(vOutput);
if (name == "checks") { if (name == "checks") {
state->forceAttrs(vProvide); state->forceAttrs(vOutput);
for (auto & aCheck : *vProvide.attrs) for (auto & attr : *vOutput.attrs)
drvPaths.insert(checkDerivation( drvPaths.insert(checkDerivation(
name + "." + (std::string) aCheck.name, *aCheck.value)); name + "." + (std::string) attr.name, *attr.value, *attr.pos));
} }
else if (name == "packages") { else if (name == "packages") {
state->forceAttrs(vProvide); state->forceAttrs(vOutput);
for (auto & aCheck : *vProvide.attrs) for (auto & attr : *vOutput.attrs)
checkDerivation( checkDerivation(
name + "." + (std::string) aCheck.name, *aCheck.value); name + "." + (std::string) attr.name, *attr.value, *attr.pos);
} }
else if (name == "apps") { else if (name == "apps") {
state->forceAttrs(vProvide); state->forceAttrs(vOutput);
for (auto & aCheck : *vProvide.attrs) for (auto & attr : *vOutput.attrs)
checkApp( checkApp(
name + "." + (std::string) aCheck.name, *aCheck.value); name + "." + (std::string) attr.name, *attr.value, *attr.pos);
} }
else if (name == "defaultPackage" || name == "devShell") else if (name == "defaultPackage" || name == "devShell")
checkDerivation(name, vProvide); checkDerivation(name, vOutput, pos);
else if (name == "defaultApp") else if (name == "defaultApp")
checkApp(name, vProvide); checkApp(name, vOutput, pos);
else if (name == "legacyPackages") else if (name == "legacyPackages")
// FIXME: do getDerivations? // FIXME: do getDerivations?
; ;
else if (name == "overlay") else if (name == "overlay")
checkOverlay(name, vProvide); checkOverlay(name, vOutput, pos);
else if (name == "overlays") {
state->forceAttrs(vOutput);
for (auto & attr : *vOutput.attrs)
checkOverlay(name + "." + (std::string) attr.name,
*attr.value, *attr.pos);
}
else if (name == "nixosModule")
checkModule(name, vOutput, pos);
else if (name == "nixosModules") {
state->forceAttrs(vOutput);
for (auto & attr : *vOutput.attrs)
checkModule(name + "." + (std::string) attr.name,
*attr.value, *attr.pos);
}
else else
warn("unknown flake output '%s'", name); warn("unknown flake output '%s'", name);