Add pure evaluation mode
In this mode, the following restrictions apply: * The builtins currentTime, currentSystem and storePath throw an error. * $NIX_PATH and -I are ignored. * fetchGit and fetchMercurial require a revision hash. * fetchurl and fetchTarball require a sha256 attribute. * No file system access is allowed outside of the paths returned by fetch{Git,Mercurial,url,Tarball}. Thus 'nix build -f ./foo.nix' is not allowed. Thus, the evaluation result is completely reproducible from the command line arguments. E.g. nix build --pure-eval '( let nix = fetchGit { url = https://github.com/NixOS/nixpkgs.git; rev = "9c927de4b179a6dd210dd88d34bda8af4b575680"; }; nixpkgs = fetchGit { url = https://github.com/NixOS/nixpkgs.git; ref = "release-17.09"; rev = "66b4de79e3841530e6d9c6baf98702aa1f7124e4"; }; in (import (nix + "/release.nix") { inherit nix nixpkgs; }).build.x86_64-linux )' The goal is to enable completely reproducible and traceable evaluation. For example, a NixOS configuration could be fully described by a single Git commit hash. 'nixos-rebuild' would do something like nix build --pure-eval '( (import (fetchGit { url = file:///my-nixos-config; rev = "..."; })).system ') where the Git repository /my-nixos-config would use further fetchGit calls or Git externals to fetch Nixpkgs and whatever other dependencies it has. Either way, the commit hash would uniquely identify the NixOS configuration and allow it to reproduced.
This commit is contained in:
parent
23fa7e3606
commit
d4dcffd643
19 changed files with 159 additions and 53 deletions
|
@ -39,7 +39,7 @@ installcheck:
|
||||||
echo "$${red}$$failed out of $$total tests failed $$normal"; \
|
echo "$${red}$$failed out of $$total tests failed $$normal"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
else \
|
else \
|
||||||
echo "$${green}All tests succeeded"; \
|
echo "$${green}All tests succeeded$$normal"; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: check installcheck
|
.PHONY: check installcheck
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
pkgs = import nixpkgs {};
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
|
|
||||||
jobs = rec {
|
jobs = rec {
|
||||||
|
|
||||||
|
|
|
@ -300,16 +300,25 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||||
{
|
{
|
||||||
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||||
|
|
||||||
restricted = settings.restrictEval;
|
|
||||||
|
|
||||||
assert(gcInitialised);
|
assert(gcInitialised);
|
||||||
|
|
||||||
/* Initialise the Nix expression search path. */
|
/* Initialise the Nix expression search path. */
|
||||||
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
|
if (!settings.pureEval) {
|
||||||
for (auto & i : _searchPath) addToSearchPath(i);
|
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
|
||||||
for (auto & i : paths) addToSearchPath(i);
|
for (auto & i : _searchPath) addToSearchPath(i);
|
||||||
|
for (auto & i : paths) addToSearchPath(i);
|
||||||
|
}
|
||||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||||
|
|
||||||
|
if (settings.restrictEval || settings.pureEval) {
|
||||||
|
allowedPaths = PathSet();
|
||||||
|
for (auto & i : searchPath) {
|
||||||
|
auto r = resolveSearchPathElem(i);
|
||||||
|
if (!r.first) continue;
|
||||||
|
allowedPaths->insert(r.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clearValue(vEmptySet);
|
clearValue(vEmptySet);
|
||||||
vEmptySet.type = tAttrs;
|
vEmptySet.type = tAttrs;
|
||||||
vEmptySet.attrs = allocBindings(0);
|
vEmptySet.attrs = allocBindings(0);
|
||||||
|
@ -326,38 +335,39 @@ EvalState::~EvalState()
|
||||||
|
|
||||||
Path EvalState::checkSourcePath(const Path & path_)
|
Path EvalState::checkSourcePath(const Path & path_)
|
||||||
{
|
{
|
||||||
if (!restricted) return path_;
|
if (!allowedPaths) return path_;
|
||||||
|
|
||||||
|
auto doThrow = [&]() [[noreturn]] {
|
||||||
|
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path_);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (auto & i : *allowedPaths) {
|
||||||
|
if (isDirOrInDir(path_, i)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) doThrow();
|
||||||
|
|
||||||
/* Resolve symlinks. */
|
/* Resolve symlinks. */
|
||||||
debug(format("checking access to '%s'") % path_);
|
debug(format("checking access to '%s'") % path_);
|
||||||
Path path = canonPath(path_, true);
|
Path path = canonPath(path_, true);
|
||||||
|
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : *allowedPaths) {
|
||||||
auto r = resolveSearchPathElem(i);
|
if (isDirOrInDir(path, i))
|
||||||
if (!r.first) continue;
|
|
||||||
if (path == r.second || isInDir(path, r.second))
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To support import-from-derivation, allow access to anything in
|
doThrow();
|
||||||
the store. FIXME: only allow access to paths that have been
|
|
||||||
constructed by this evaluation. */
|
|
||||||
if (store->isInStore(path)) return path;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Hack to support the chroot dependencies of corepkgs (see
|
|
||||||
corepkgs/config.nix.in). */
|
|
||||||
if (path == settings.nixPrefix && isStorePath(settings.nixPrefix))
|
|
||||||
return path;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::checkURI(const std::string & uri)
|
void EvalState::checkURI(const std::string & uri)
|
||||||
{
|
{
|
||||||
if (!restricted) return;
|
if (!settings.restrictEval) return;
|
||||||
|
|
||||||
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
||||||
prefix. Thus, the prefix https://github.co does not permit
|
prefix. Thus, the prefix https://github.co does not permit
|
||||||
|
@ -396,7 +406,7 @@ void EvalState::addConstant(const string & name, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::addPrimOp(const string & name,
|
Value * EvalState::addPrimOp(const string & name,
|
||||||
unsigned int arity, PrimOpFun primOp)
|
unsigned int arity, PrimOpFun primOp)
|
||||||
{
|
{
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
|
@ -407,6 +417,7 @@ void EvalState::addPrimOp(const string & name,
|
||||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -659,8 +670,10 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::evalFile(const Path & path, Value & v)
|
void EvalState::evalFile(const Path & path_, Value & v)
|
||||||
{
|
{
|
||||||
|
auto path = checkSourcePath(path_);
|
||||||
|
|
||||||
FileEvalCache::iterator i;
|
FileEvalCache::iterator i;
|
||||||
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
||||||
v = i->second;
|
v = i->second;
|
||||||
|
|
|
@ -76,9 +76,9 @@ public:
|
||||||
already exist there. */
|
already exist there. */
|
||||||
RepairFlag repair;
|
RepairFlag repair;
|
||||||
|
|
||||||
/* If set, don't allow access to files outside of the Nix search
|
/* The allowed filesystem paths in restricted or pure evaluation
|
||||||
path or to environment variables. */
|
mode. */
|
||||||
bool restricted;
|
std::experimental::optional<PathSet> allowedPaths;
|
||||||
|
|
||||||
Value vEmptySet;
|
Value vEmptySet;
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ private:
|
||||||
|
|
||||||
void addConstant(const string & name, Value & v);
|
void addConstant(const string & name, Value & v);
|
||||||
|
|
||||||
void addPrimOp(const string & name,
|
Value * addPrimOp(const string & name,
|
||||||
unsigned int arity, PrimOpFun primOp);
|
unsigned int arity, PrimOpFun primOp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -439,7 +439,7 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val
|
||||||
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string name = state.forceStringNoCtx(*args[0], pos);
|
string name = state.forceStringNoCtx(*args[0], pos);
|
||||||
mkString(v, state.restricted ? "" : getEnv(name));
|
mkString(v, settings.restrictEval || settings.pureEval ? "" : getEnv(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1929,7 +1929,14 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
|
|
||||||
state.checkURI(url);
|
state.checkURI(url);
|
||||||
|
|
||||||
|
if (settings.pureEval && !expectedHash)
|
||||||
|
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
|
||||||
|
|
||||||
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
|
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
|
||||||
|
|
||||||
|
if (state.allowedPaths)
|
||||||
|
state.allowedPaths->insert(res);
|
||||||
|
|
||||||
mkString(v, res, PathSet({res}));
|
mkString(v, res, PathSet({res}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,11 +1988,28 @@ void EvalState::createBaseEnv()
|
||||||
mkNull(v);
|
mkNull(v);
|
||||||
addConstant("null", v);
|
addConstant("null", v);
|
||||||
|
|
||||||
mkInt(v, time(0));
|
auto vThrow = addPrimOp("throw", 1, prim_throw);
|
||||||
addConstant("__currentTime", v);
|
|
||||||
|
|
||||||
mkString(v, settings.thisSystem);
|
auto addPurityError = [&](const std::string & name) {
|
||||||
addConstant("__currentSystem", v);
|
Value * v2 = allocValue();
|
||||||
|
mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
|
||||||
|
mkApp(v, *vThrow, *v2);
|
||||||
|
addConstant(name, v);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.pureEval)
|
||||||
|
addPurityError("__currentTime");
|
||||||
|
else {
|
||||||
|
mkInt(v, time(0));
|
||||||
|
addConstant("__currentTime", v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.pureEval)
|
||||||
|
addPurityError("__currentSystem");
|
||||||
|
else {
|
||||||
|
mkString(v, settings.thisSystem);
|
||||||
|
addConstant("__currentSystem", v);
|
||||||
|
}
|
||||||
|
|
||||||
mkString(v, nixVersion);
|
mkString(v, nixVersion);
|
||||||
addConstant("__nixVersion", v);
|
addConstant("__nixVersion", v);
|
||||||
|
@ -2001,10 +2025,10 @@ void EvalState::createBaseEnv()
|
||||||
addConstant("__langVersion", v);
|
addConstant("__langVersion", v);
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
addPrimOp("scopedImport", 2, prim_scopedImport);
|
auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
|
||||||
Value * v2 = allocValue();
|
Value * v2 = allocValue();
|
||||||
mkAttrs(*v2, 0);
|
mkAttrs(*v2, 0);
|
||||||
mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2);
|
mkApp(v, *vScopedImport, *v2);
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
addConstant("import", v);
|
addConstant("import", v);
|
||||||
if (settings.enableNativeCode) {
|
if (settings.enableNativeCode) {
|
||||||
|
@ -2020,7 +2044,6 @@ void EvalState::createBaseEnv()
|
||||||
addPrimOp("__isBool", 1, prim_isBool);
|
addPrimOp("__isBool", 1, prim_isBool);
|
||||||
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
||||||
addPrimOp("abort", 1, prim_abort);
|
addPrimOp("abort", 1, prim_abort);
|
||||||
addPrimOp("throw", 1, prim_throw);
|
|
||||||
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
||||||
addPrimOp("__tryEval", 1, prim_tryEval);
|
addPrimOp("__tryEval", 1, prim_tryEval);
|
||||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||||
|
@ -2035,7 +2058,10 @@ void EvalState::createBaseEnv()
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
addPrimOp("__toPath", 1, prim_toPath);
|
addPrimOp("__toPath", 1, prim_toPath);
|
||||||
addPrimOp("__storePath", 1, prim_storePath);
|
if (settings.pureEval)
|
||||||
|
addPurityError("__storePath");
|
||||||
|
else
|
||||||
|
addPrimOp("__storePath", 1, prim_storePath);
|
||||||
addPrimOp("__pathExists", 1, prim_pathExists);
|
addPrimOp("__pathExists", 1, prim_pathExists);
|
||||||
addPrimOp("baseNameOf", 1, prim_baseNameOf);
|
addPrimOp("baseNameOf", 1, prim_baseNameOf);
|
||||||
addPrimOp("dirOf", 1, prim_dirOf);
|
addPrimOp("dirOf", 1, prim_dirOf);
|
||||||
|
|
|
@ -22,10 +22,15 @@ struct GitInfo
|
||||||
uint64_t revCount = 0;
|
uint64_t revCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
||||||
|
|
||||||
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||||
std::experimental::optional<std::string> ref, std::string rev,
|
std::experimental::optional<std::string> ref, std::string rev,
|
||||||
const std::string & name)
|
const std::string & name)
|
||||||
{
|
{
|
||||||
|
if (settings.pureEval && rev == "")
|
||||||
|
throw Error("in pure evaluation mode, 'fetchGit' requires a Git revision");
|
||||||
|
|
||||||
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
|
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
|
||||||
|
|
||||||
bool clean = true;
|
bool clean = true;
|
||||||
|
@ -76,11 +81,8 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||||
|
|
||||||
if (!ref) ref = "master"s;
|
if (!ref) ref = "master"s;
|
||||||
|
|
||||||
if (rev != "") {
|
if (rev != "" && !std::regex_match(rev, revRegex))
|
||||||
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
throw Error("invalid Git revision '%s'", rev);
|
||||||
if (!std::regex_match(rev, revRegex))
|
|
||||||
throw Error("invalid Git revision '%s'", rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path cacheDir = getCacheDir() + "/nix/git";
|
Path cacheDir = getCacheDir() + "/nix/git";
|
||||||
|
|
||||||
|
@ -231,6 +233,9 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
|
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
|
||||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
|
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
|
|
||||||
|
if (state.allowedPaths)
|
||||||
|
state.allowedPaths->insert(gitInfo.storePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
|
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
|
||||||
|
|
|
@ -27,6 +27,9 @@ std::regex commitHashRegex("^[0-9a-fA-F]{40}$");
|
||||||
HgInfo exportMercurial(ref<Store> store, const std::string & uri,
|
HgInfo exportMercurial(ref<Store> store, const std::string & uri,
|
||||||
std::string rev, const std::string & name)
|
std::string rev, const std::string & name)
|
||||||
{
|
{
|
||||||
|
if (settings.pureEval && rev == "")
|
||||||
|
throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision");
|
||||||
|
|
||||||
if (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
|
if (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
|
||||||
|
|
||||||
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
|
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
|
||||||
|
@ -196,6 +199,9 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(hgInfo.rev, 0, 12));
|
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(hgInfo.rev, 0, 12));
|
||||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), hgInfo.revCount);
|
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), hgInfo.revCount);
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
|
|
||||||
|
if (state.allowedPaths)
|
||||||
|
state.allowedPaths->insert(hgInfo.storePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
|
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
|
||||||
|
|
|
@ -232,6 +232,9 @@ public:
|
||||||
"Whether to restrict file system access to paths in $NIX_PATH, "
|
"Whether to restrict file system access to paths in $NIX_PATH, "
|
||||||
"and network access to the URI prefixes listed in 'allowed-uris'."};
|
"and network access to the URI prefixes listed in 'allowed-uris'."};
|
||||||
|
|
||||||
|
Setting<bool> pureEval{this, false, "pure-eval",
|
||||||
|
"Whether to restrict file system and network access to files specified by cryptographic hash."};
|
||||||
|
|
||||||
Setting<size_t> buildRepeat{this, 0, "repeat",
|
Setting<size_t> buildRepeat{this, 0, "repeat",
|
||||||
"The number of times to repeat a build in order to verify determinism.",
|
"The number of times to repeat a build in order to verify determinism.",
|
||||||
{"build-repeat"}};
|
{"build-repeat"}};
|
||||||
|
|
|
@ -192,6 +192,12 @@ bool isInDir(const Path & path, const Path & dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isDirOrInDir(const Path & path, const Path & dir)
|
||||||
|
{
|
||||||
|
return path == dir or isInDir(path, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct stat lstat(const Path & path)
|
struct stat lstat(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
|
@ -53,10 +53,12 @@ Path dirOf(const Path & path);
|
||||||
following the final `/'. */
|
following the final `/'. */
|
||||||
string baseNameOf(const Path & path);
|
string baseNameOf(const Path & path);
|
||||||
|
|
||||||
/* Check whether a given path is a descendant of the given
|
/* Check whether 'path' is a descendant of 'dir'. */
|
||||||
directory. */
|
|
||||||
bool isInDir(const Path & path, const Path & dir);
|
bool isInDir(const Path & path, const Path & dir);
|
||||||
|
|
||||||
|
/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */
|
||||||
|
bool isDirOrInDir(const Path & path, const Path & dir);
|
||||||
|
|
||||||
/* Get status of `path'. */
|
/* Get status of `path'. */
|
||||||
struct stat lstat(const Path & path);
|
struct stat lstat(const Path & path);
|
||||||
|
|
||||||
|
|
|
@ -279,8 +279,8 @@ void mainWrapped(int argc, char * * argv)
|
||||||
else
|
else
|
||||||
/* If we're in a #! script, interpret filenames
|
/* If we're in a #! script, interpret filenames
|
||||||
relative to the script. */
|
relative to the script. */
|
||||||
exprs.push_back(state.parseExprFromFile(resolveExprPath(lookupFileArg(state,
|
exprs.push_back(state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state,
|
||||||
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))));
|
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Evaluate them into derivations. */
|
/* Evaluate them into derivations. */
|
||||||
|
|
|
@ -182,7 +182,7 @@ int main(int argc, char * * argv)
|
||||||
for (auto & i : files) {
|
for (auto & i : files) {
|
||||||
Expr * e = fromArgs
|
Expr * e = fromArgs
|
||||||
? state.parseExprFromString(i, absPath("."))
|
? state.parseExprFromString(i, absPath("."))
|
||||||
: state.parseExprFromFile(resolveExprPath(lookupFileArg(state, i)));
|
: state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state, i))));
|
||||||
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
||||||
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,17 @@ rev2=$(git -C $repo rev-parse HEAD)
|
||||||
path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
|
path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
|
||||||
[[ $(cat $path/hello) = world ]]
|
[[ $(cat $path/hello) = world ]]
|
||||||
|
|
||||||
|
# In pure eval mode, fetchGit without a revision should fail.
|
||||||
|
[[ $(nix eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))") = world ]]
|
||||||
|
(! nix eval --pure-eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))")
|
||||||
|
|
||||||
# Fetch using an explicit revision hash.
|
# Fetch using an explicit revision hash.
|
||||||
path2=$(nix eval --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
|
path2=$(nix eval --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
|
||||||
[[ $path = $path2 ]]
|
[[ $path = $path2 ]]
|
||||||
|
|
||||||
|
# In pure eval mode, fetchGit with a revision should succeed.
|
||||||
|
[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchGit { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
|
||||||
|
|
||||||
# Fetch again. This should be cached.
|
# Fetch again. This should be cached.
|
||||||
mv $repo ${repo}-tmp
|
mv $repo ${repo}-tmp
|
||||||
path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
|
path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
|
||||||
|
|
|
@ -29,10 +29,17 @@ rev2=$(hg log --cwd $repo -r tip --template '{node}')
|
||||||
path=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
|
path=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
|
||||||
[[ $(cat $path/hello) = world ]]
|
[[ $(cat $path/hello) = world ]]
|
||||||
|
|
||||||
|
# In pure eval mode, fetchGit without a revision should fail.
|
||||||
|
[[ $(nix eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))") = world ]]
|
||||||
|
(! nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))")
|
||||||
|
|
||||||
# Fetch using an explicit revision hash.
|
# Fetch using an explicit revision hash.
|
||||||
path2=$(nix eval --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
|
path2=$(nix eval --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
|
||||||
[[ $path = $path2 ]]
|
[[ $path = $path2 ]]
|
||||||
|
|
||||||
|
# In pure eval mode, fetchGit with a revision should succeed.
|
||||||
|
[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
|
||||||
|
|
||||||
# Fetch again. This should be cached.
|
# Fetch again. This should be cached.
|
||||||
mv $repo ${repo}-tmp
|
mv $repo ${repo}-tmp
|
||||||
path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
|
path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
|
||||||
|
|
|
@ -20,7 +20,8 @@ nix_tests = \
|
||||||
fetchMercurial.sh \
|
fetchMercurial.sh \
|
||||||
signing.sh \
|
signing.sh \
|
||||||
run.sh \
|
run.sh \
|
||||||
brotli.sh
|
brotli.sh \
|
||||||
|
pure-eval.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
|
||||||
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
||||||
|
|
3
tests/pure-eval.nix
Normal file
3
tests/pure-eval.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
x = 123;
|
||||||
|
}
|
18
tests/pure-eval.sh
Normal file
18
tests/pure-eval.sh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
nix eval --pure-eval '(assert 1 + 2 == 3; true)'
|
||||||
|
|
||||||
|
[[ $(nix eval '(builtins.readFile ./pure-eval.sh)') =~ clearStore ]]
|
||||||
|
|
||||||
|
(! nix eval --pure-eval '(builtins.readFile ./pure-eval.sh)')
|
||||||
|
|
||||||
|
(! nix eval --pure-eval '(builtins.currentTime)')
|
||||||
|
(! nix eval --pure-eval '(builtins.currentSystem)')
|
||||||
|
|
||||||
|
(! nix-instantiate --pure-eval ./simple.nix)
|
||||||
|
|
||||||
|
[[ $(nix eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)") == 123 ]]
|
||||||
|
(! nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)")
|
||||||
|
nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; sha256 = \"$(nix hash-file pure-eval.nix --type sha256)\"; })).x)"
|
1
tests/restricted.nix
Normal file
1
tests/restricted.nix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1 + 2
|
|
@ -3,7 +3,8 @@ source common.sh
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
nix-instantiate --restrict-eval --eval -E '1 + 2'
|
nix-instantiate --restrict-eval --eval -E '1 + 2'
|
||||||
(! nix-instantiate --restrict-eval ./simple.nix)
|
(! nix-instantiate --restrict-eval ./restricted.nix)
|
||||||
|
(! nix-instantiate --eval --restrict-eval <(echo '1 + 2'))
|
||||||
nix-instantiate --restrict-eval ./simple.nix -I src=.
|
nix-instantiate --restrict-eval ./simple.nix -I src=.
|
||||||
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
|
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
|
||||||
|
|
||||||
|
@ -28,3 +29,10 @@ nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval
|
||||||
(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
|
(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
|
||||||
(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
|
(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
|
||||||
(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)
|
(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)
|
||||||
|
|
||||||
|
ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix
|
||||||
|
[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
|
||||||
|
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix)
|
||||||
|
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT)
|
||||||
|
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .)
|
||||||
|
nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
|
||||||
|
|
Loading…
Reference in a new issue