mirror of
https://github.com/NixOS/nix
synced 2025-07-06 21:41:48 +02:00
Merge branch 'read-only-local-store' into overlayfs-store
This commit is contained in:
commit
b852bdb3f8
37 changed files with 360 additions and 154 deletions
|
@ -2620,7 +2620,7 @@ Strings EvalSettings::getDefaultNixPath()
|
|||
{
|
||||
Strings res;
|
||||
auto add = [&](const Path & p, const std::string & s = std::string()) {
|
||||
if (pathExists(p)) {
|
||||
if (pathAccessible(p)) {
|
||||
if (s.empty()) {
|
||||
res.push_back(p);
|
||||
} else {
|
||||
|
|
|
@ -745,7 +745,13 @@ struct EvalSettings : Config
|
|||
)"};
|
||||
|
||||
Setting<bool> pureEval{this, false, "pure-eval",
|
||||
"Whether to restrict file system and network access to files specified by cryptographic hash."};
|
||||
R"(
|
||||
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
||||
|
||||
- Restrict file system and network access to files specified by cryptographic hash
|
||||
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
|
||||
)"
|
||||
};
|
||||
|
||||
Setting<bool> enableImportFromDerivation{
|
||||
this, true, "allow-import-from-derivation",
|
||||
|
|
|
@ -1152,16 +1152,14 @@ drvName, Bindings * attrs, Value & v)
|
|||
if (i->value->type() == nNull) continue;
|
||||
}
|
||||
|
||||
if (i->name == state.sContentAddressed) {
|
||||
contentAddressed = state.forceBool(*i->value, noPos, context_below);
|
||||
if (contentAddressed)
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
if (i->name == state.sContentAddressed && state.forceBool(*i->value, noPos, context_below)) {
|
||||
contentAddressed = true;
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
}
|
||||
|
||||
else if (i->name == state.sImpure) {
|
||||
isImpure = state.forceBool(*i->value, noPos, context_below);
|
||||
if (isImpure)
|
||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
else if (i->name == state.sImpure && state.forceBool(*i->value, noPos, context_below)) {
|
||||
isImpure = true;
|
||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
}
|
||||
|
||||
/* The `args' attribute is special: it supplies the
|
||||
|
@ -1503,7 +1501,7 @@ static RegisterPrimOp primop_storePath({
|
|||
causes the path to be *copied* again to the Nix store, resulting
|
||||
in a new path (e.g. `/nix/store/ld01dnzc…-source-source`).
|
||||
|
||||
This function is not available in pure evaluation mode.
|
||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
)",
|
||||
.fun = prim_storePath,
|
||||
});
|
||||
|
@ -3910,13 +3908,8 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
|
|||
for (auto elem : args[0]->listItems())
|
||||
from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings"));
|
||||
|
||||
std::vector<std::pair<std::string, NixStringContext>> to;
|
||||
to.reserve(args[1]->listSize());
|
||||
for (auto elem : args[1]->listItems()) {
|
||||
NixStringContext ctx;
|
||||
auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
|
||||
to.emplace_back(s, std::move(ctx));
|
||||
}
|
||||
std::unordered_map<size_t, std::string> cache;
|
||||
auto to = args[1]->listItems();
|
||||
|
||||
NixStringContext context;
|
||||
auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings");
|
||||
|
@ -3927,10 +3920,19 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
|
|||
bool found = false;
|
||||
auto i = from.begin();
|
||||
auto j = to.begin();
|
||||
for (; i != from.end(); ++i, ++j)
|
||||
size_t j_index = 0;
|
||||
for (; i != from.end(); ++i, ++j, ++j_index)
|
||||
if (s.compare(p, i->size(), *i) == 0) {
|
||||
found = true;
|
||||
res += j->first;
|
||||
auto v = cache.find(j_index);
|
||||
if (v == cache.end()) {
|
||||
NixStringContext ctx;
|
||||
auto ts = state.forceString(**j, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
|
||||
v = (cache.emplace(j_index, ts)).first;
|
||||
for (auto& path : ctx)
|
||||
context.insert(path);
|
||||
}
|
||||
res += v->second;
|
||||
if (i->empty()) {
|
||||
if (p < s.size())
|
||||
res += s[p];
|
||||
|
@ -3938,9 +3940,6 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
|
|||
} else {
|
||||
p += i->size();
|
||||
}
|
||||
for (auto& path : j->second)
|
||||
context.insert(path);
|
||||
j->second.clear();
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
|
@ -3958,7 +3957,11 @@ static RegisterPrimOp primop_replaceStrings({
|
|||
.args = {"from", "to", "s"},
|
||||
.doc = R"(
|
||||
Given string *s*, replace every occurrence of the strings in *from*
|
||||
with the corresponding string in *to*. For example,
|
||||
with the corresponding string in *to*.
|
||||
|
||||
The argument *to* is lazy, that is, it is only evaluated when its corresponding pattern in *from* is matched in the string *s*
|
||||
|
||||
Example:
|
||||
|
||||
```nix
|
||||
builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"
|
||||
|
|
|
@ -286,9 +286,9 @@ static RegisterPrimOp primop_fetchurl({
|
|||
.name = "__fetchurl",
|
||||
.args = {"url"},
|
||||
.doc = R"(
|
||||
Download the specified URL and return the path of the downloaded
|
||||
file. This function is not available if [restricted evaluation
|
||||
mode](../command-ref/conf-file.md) is enabled.
|
||||
Download the specified URL and return the path of the downloaded file.
|
||||
|
||||
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
|
||||
)",
|
||||
.fun = prim_fetchurl,
|
||||
});
|
||||
|
@ -338,8 +338,7 @@ static RegisterPrimOp primop_fetchTarball({
|
|||
stdenv.mkDerivation { … }
|
||||
```
|
||||
|
||||
This function is not available if [restricted evaluation
|
||||
mode](../command-ref/conf-file.md) is enabled.
|
||||
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
|
||||
)",
|
||||
.fun = prim_fetchTarball,
|
||||
});
|
||||
|
@ -470,14 +469,9 @@ static RegisterPrimOp primop_fetchGit({
|
|||
}
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Nix will refetch the branch in accordance with
|
||||
> the option `tarball-ttl`.
|
||||
Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This behavior is disabled in *Pure evaluation mode*.
|
||||
This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
|
||||
- To fetch the content of a checked-out work directory:
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ namespace nix {
|
|||
hintfmt("value is %s while a string was expected", "an integer"),
|
||||
hintfmt("while evaluating one of the strings to replace passed to builtins.replaceStrings"));
|
||||
|
||||
ASSERT_TRACE2("replaceStrings [ \"old\" ] [ true ] {}",
|
||||
ASSERT_TRACE2("replaceStrings [ \"oo\" ] [ true ] \"foo\"",
|
||||
TypeError,
|
||||
hintfmt("value is %s while a string was expected", "a Boolean"),
|
||||
hintfmt("while evaluating one of the replacement strings passed to builtins.replaceStrings"));
|
||||
|
|
|
@ -75,22 +75,28 @@ SourcePath SourcePath::resolveSymlinks() const
|
|||
|
||||
int linksAllowed = 1024;
|
||||
|
||||
for (auto & component : path) {
|
||||
res.path.push(component);
|
||||
while (true) {
|
||||
if (auto st = res.maybeLstat()) {
|
||||
std::list<std::string> todo;
|
||||
for (auto & c : path)
|
||||
todo.push_back(std::string(c));
|
||||
|
||||
while (!todo.empty()) {
|
||||
auto c = *todo.begin();
|
||||
todo.pop_front();
|
||||
if (c == "" || c == ".")
|
||||
;
|
||||
else if (c == "..")
|
||||
res.path.pop();
|
||||
else {
|
||||
res.path.push(c);
|
||||
if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
|
||||
if (!linksAllowed--)
|
||||
throw Error("infinite symlink recursion in path '%s'", path);
|
||||
if (st->type != InputAccessor::tSymlink) break;
|
||||
auto target = res.readLink();
|
||||
res.path.pop();
|
||||
if (hasPrefix(target, "/"))
|
||||
res = CanonPath(target);
|
||||
else {
|
||||
res.path.pop();
|
||||
res.path.extend(CanonPath(target));
|
||||
}
|
||||
} else
|
||||
break;
|
||||
res.path = CanonPath::root;
|
||||
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -357,7 +357,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
|
|||
for (auto & [_, status] : initialOutputs) {
|
||||
if (!status.known) continue;
|
||||
if (buildMode != bmCheck && status.known->isValid()) continue;
|
||||
auto p = worker.store.printStorePath(status.known->path);
|
||||
auto p = worker.store.toRealPath(status.known->path);
|
||||
if (pathExists(chrootRootDir + p))
|
||||
renameFile((chrootRootDir + p), p);
|
||||
}
|
||||
|
@ -1772,7 +1772,8 @@ void LocalDerivationGoal::runChild()
|
|||
if (pathExists(path))
|
||||
ss.push_back(path);
|
||||
|
||||
dirsInChroot.emplace(settings.caFile, "/etc/ssl/certs/ca-certificates.crt");
|
||||
if (settings.caFile != "")
|
||||
dirsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", settings.caFile, true);
|
||||
}
|
||||
|
||||
for (auto & i : ss) dirsInChroot.emplace(i, i);
|
||||
|
|
|
@ -183,7 +183,7 @@ bool Settings::isWSL1()
|
|||
Path Settings::getDefaultSSLCertFile()
|
||||
{
|
||||
for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"})
|
||||
if (pathExists(fn)) return fn;
|
||||
if (pathAccessible(fn)) return fn;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -1014,6 +1014,18 @@ public:
|
|||
| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` |
|
||||
| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` |
|
||||
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
|
||||
|
||||
If you already have Nix installed and are using [profiles](@docroot@/package-management/profiles.md) or [channels](@docroot@/package-management/channels.md), you should migrate manually when you enable this option.
|
||||
If `$XDG_STATE_HOME` is not set, use `$HOME/.local/state/nix` instead of `$XDG_STATE_HOME/nix`.
|
||||
This can be achieved with the following shell commands:
|
||||
|
||||
```sh
|
||||
nix_state_home=${XDG_STATE_HOME-$HOME/.local/state}/nix
|
||||
mkdir -p $nix_state_home
|
||||
mv $HOME/.nix-profile $nix_state_home/profile
|
||||
mv $HOME/.nix-defexpr $nix_state_home/defexpr
|
||||
mv $HOME/.nix-channels $nix_state_home/channels
|
||||
```
|
||||
)"
|
||||
};
|
||||
};
|
||||
|
|
|
@ -190,7 +190,11 @@ LocalStore::LocalStore(const Params & params)
|
|||
|
||||
/* Create missing state directories if they don't already exist. */
|
||||
createDirs(realStoreDir);
|
||||
makeStoreWritable();
|
||||
if (readOnly) {
|
||||
experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
|
||||
} else {
|
||||
makeStoreWritable();
|
||||
}
|
||||
createDirs(linksDir);
|
||||
Path profilesDir = stateDir + "/profiles";
|
||||
createDirs(profilesDir);
|
||||
|
@ -202,10 +206,6 @@ LocalStore::LocalStore(const Params & params)
|
|||
createSymlink(profilesDir, gcRootsDir + "/profiles");
|
||||
}
|
||||
|
||||
if (readOnly) {
|
||||
experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
|
||||
}
|
||||
|
||||
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
|
||||
createDirs(perUserDir);
|
||||
if (!readOnly) {
|
||||
|
|
|
@ -9,8 +9,8 @@ static void checkName(std::string_view path, std::string_view name)
|
|||
if (name.empty())
|
||||
throw BadStorePath("store path '%s' has an empty name", path);
|
||||
if (name.size() > StorePath::MaxPathLen)
|
||||
throw BadStorePath("store path '%s' has a name longer than '%d characters",
|
||||
StorePath::MaxPathLen, path);
|
||||
throw BadStorePath("store path '%s' has a name longer than %d characters",
|
||||
path, StorePath::MaxPathLen);
|
||||
// See nameRegexStr for the definition
|
||||
for (auto c : name)
|
||||
if (!((c >= '0' && c <= '9')
|
||||
|
|
|
@ -50,6 +50,8 @@ constexpr std::array<ExperimentalFeatureDetails, 14> xpFeatureDetails = {{
|
|||
or other impure derivations can rely on impure derivations. Finally,
|
||||
an impure derivation cannot also be
|
||||
[content-addressed](#xp-feature-ca-derivations).
|
||||
|
||||
This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime).
|
||||
)",
|
||||
},
|
||||
{
|
||||
|
|
|
@ -202,7 +202,7 @@ namespace nix {
|
|||
}
|
||||
|
||||
TEST(pathExists, bogusPathDoesNotExist) {
|
||||
ASSERT_FALSE(pathExists("/home/schnitzel/darmstadt/pommes"));
|
||||
ASSERT_FALSE(pathExists("/schnitzel/darmstadt/pommes"));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
|
|
@ -266,6 +266,17 @@ bool pathExists(const Path & path)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool pathAccessible(const Path & path)
|
||||
{
|
||||
try {
|
||||
return pathExists(path);
|
||||
} catch (SysError & e) {
|
||||
// swallow EPERM
|
||||
if (e.errNo == EPERM) return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Path readLink(const Path & path)
|
||||
{
|
||||
|
|
|
@ -120,6 +120,14 @@ struct stat lstat(const Path & path);
|
|||
*/
|
||||
bool pathExists(const Path & path);
|
||||
|
||||
/**
|
||||
* A version of pathExists that returns false on a permission error.
|
||||
* Useful for inferring default paths across directories that might not
|
||||
* be readable.
|
||||
* @return true iff the given path can be accessed and exists
|
||||
*/
|
||||
bool pathAccessible(const Path & path);
|
||||
|
||||
/**
|
||||
* Read the contents (target) of a symbolic link. The result is not
|
||||
* in any way canonicalised.
|
||||
|
|
121
src/nix/flake.cc
121
src/nix/flake.cc
|
@ -259,6 +259,7 @@ struct CmdFlakeInfo : CmdFlakeMetadata
|
|||
struct CmdFlakeCheck : FlakeCommand
|
||||
{
|
||||
bool build = true;
|
||||
bool checkAllSystems = false;
|
||||
|
||||
CmdFlakeCheck()
|
||||
{
|
||||
|
@ -267,6 +268,11 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
.description = "Do not build checks.",
|
||||
.handler = {&build, false}
|
||||
});
|
||||
addFlag({
|
||||
.longName = "all-systems",
|
||||
.description = "Check the outputs for all systems.",
|
||||
.handler = {&checkAllSystems, true}
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
|
@ -292,6 +298,7 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
|
||||
lockFlags.applyNixConfig = true;
|
||||
auto flake = lockFlake();
|
||||
auto localSystem = std::string(settings.thisSystem.get());
|
||||
|
||||
bool hasErrors = false;
|
||||
auto reportError = [&](const Error & e) {
|
||||
|
@ -307,6 +314,8 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
}
|
||||
};
|
||||
|
||||
std::set<std::string> omittedSystems;
|
||||
|
||||
// FIXME: rewrite to use EvalCache.
|
||||
|
||||
auto resolve = [&] (PosIdx p) {
|
||||
|
@ -327,6 +336,15 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
|
||||
};
|
||||
|
||||
auto checkSystemType = [&](const std::string & system, const PosIdx pos) {
|
||||
if (!checkAllSystems && system != localSystem) {
|
||||
omittedSystems.insert(system);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
|
||||
try {
|
||||
auto drvInfo = getDerivation(*state, v, false);
|
||||
|
@ -509,16 +527,18 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
auto drvPath = checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
if (drvPath && attr_name == settings.thisSystem.get()) {
|
||||
drvPaths.push_back(DerivedPath::Built {
|
||||
.drvPath = *drvPath,
|
||||
.outputs = OutputsSpec::All { },
|
||||
});
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
auto drvPath = checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
if (drvPath && attr_name == settings.thisSystem.get()) {
|
||||
drvPaths.push_back(DerivedPath::Built {
|
||||
.drvPath = *drvPath,
|
||||
.outputs = OutputsSpec::All { },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -529,9 +549,11 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,11 +562,13 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,11 +577,13 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkApp(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
checkApp(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,9 +592,11 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkDerivation(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkDerivation(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,9 +605,11 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos) ) {
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,6 +617,7 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(state->symbols[attr.name], attr.pos);
|
||||
checkSystemType(state->symbols[attr.name], attr.pos);
|
||||
// FIXME: do getDerivations?
|
||||
}
|
||||
}
|
||||
|
@ -636,9 +667,11 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
checkBundler(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkBundler(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,12 +680,14 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
for (auto & attr : *vOutput.attrs) {
|
||||
const auto & attr_name = state->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
checkBundler(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
}
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
checkBundler(
|
||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -685,7 +720,15 @@ struct CmdFlakeCheck : FlakeCommand
|
|||
}
|
||||
if (hasErrors)
|
||||
throw Error("some errors were encountered during the evaluation");
|
||||
}
|
||||
|
||||
if (!omittedSystems.empty()) {
|
||||
warn(
|
||||
"The check omitted these incompatible systems: %s\n"
|
||||
"Use '--all-systems' to check all.",
|
||||
concatStringsSep(", ", omittedSystems)
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static Strings defaultTemplateAttrPathsPrefixes{"templates."};
|
||||
|
|
|
@ -102,6 +102,7 @@ way:
|
|||
available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
|
||||
|
||||
For example, if `/foo/bar` is a git repository with the following structure:
|
||||
|
||||
```
|
||||
.
|
||||
└── baz
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue