mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-14 02:06:16 +02:00
Fix flakes
This commit is contained in:
parent
4b313ceb9e
commit
1d36d16086
8 changed files with 83 additions and 65 deletions
|
@ -89,12 +89,19 @@ static void expectType(EvalState & state, ValueType type,
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
Value * value,
|
||||||
|
const Pos & pos,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
const InputPath & lockRootPath);
|
||||||
|
|
||||||
static FlakeInput parseFlakeInput(EvalState & state,
|
static FlakeInput parseFlakeInput(
|
||||||
const std::string & inputName, Value * value, const Pos & pos,
|
EvalState & state,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
const std::string & inputName,
|
||||||
|
Value * value,
|
||||||
|
const Pos & pos,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
const InputPath & lockRootPath)
|
||||||
{
|
{
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
|
||||||
|
@ -168,8 +175,11 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
Value * value,
|
||||||
|
const Pos & pos,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
const InputPath & lockRootPath)
|
||||||
{
|
{
|
||||||
std::map<FlakeId, FlakeInput> inputs;
|
std::map<FlakeId, FlakeInput> inputs;
|
||||||
|
|
||||||
|
@ -188,38 +198,31 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Flake getFlake(
|
static Flake readFlake(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const FlakeRef & originalRef,
|
const FlakeRef & lockedRef,
|
||||||
bool allowLookup,
|
nix::ref<InputAccessor> accessor,
|
||||||
FlakeCache & flakeCache,
|
const InputPath & lockRootPath)
|
||||||
InputPath lockRootPath)
|
|
||||||
{
|
{
|
||||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
auto flakeDir = canonPath("/" + lockedRef.subdir);
|
||||||
state, originalRef, allowLookup, flakeCache);
|
SourcePath flakePath{accessor, canonPath(flakeDir + "/flake.nix")};
|
||||||
|
|
||||||
// Guard against symlink attacks.
|
if (!flakePath.pathExists())
|
||||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
throw Error("source tree referenced by '%s' does not contain a file named '%s'", lockedRef, flakePath.path);
|
||||||
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
|
||||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
|
||||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
|
||||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
|
||||||
|
|
||||||
Flake flake {
|
|
||||||
.originalRef = originalRef,
|
|
||||||
.resolvedRef = resolvedRef,
|
|
||||||
.lockedRef = lockedRef,
|
|
||||||
//.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!pathExists(flakeFile))
|
|
||||||
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
|
|
||||||
|
|
||||||
Value vInfo;
|
Value vInfo;
|
||||||
// FIXME: use accessor
|
state.evalFile(flakePath, vInfo, true);
|
||||||
state.evalFile(state.rootPath(flakeFile), vInfo, true); // FIXME: symlink attack
|
|
||||||
|
|
||||||
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
|
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(state.packPath(flakePath)), 0, 0));
|
||||||
|
|
||||||
|
Flake flake {
|
||||||
|
// FIXME
|
||||||
|
.originalRef = lockedRef,
|
||||||
|
.resolvedRef = lockedRef,
|
||||||
|
.lockedRef = lockedRef,
|
||||||
|
.accessor = accessor,
|
||||||
|
.flakePath = dirOf(flakePath.path),
|
||||||
|
};
|
||||||
|
|
||||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||||
expectType(state, nString, *description->value, *description->pos);
|
expectType(state, nString, *description->value, *description->pos);
|
||||||
|
@ -295,6 +298,19 @@ static Flake getFlake(
|
||||||
return flake;
|
return flake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Flake getFlake(
|
||||||
|
EvalState & state,
|
||||||
|
const FlakeRef & originalRef,
|
||||||
|
bool allowLookup,
|
||||||
|
FlakeCache & flakeCache,
|
||||||
|
const InputPath & lockRootPath)
|
||||||
|
{
|
||||||
|
// FIXME: resolve
|
||||||
|
auto [accessor, input] = originalRef.input.lazyFetch(state.store);
|
||||||
|
|
||||||
|
return readFlake(state, originalRef, accessor, lockRootPath);
|
||||||
|
}
|
||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
||||||
{
|
{
|
||||||
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
||||||
|
@ -306,6 +322,14 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup
|
||||||
return getFlake(state, originalRef, allowLookup, flakeCache);
|
return getFlake(state, originalRef, allowLookup, flakeCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LockFile readLockFile(const Flake & flake)
|
||||||
|
{
|
||||||
|
SourcePath lockFilePath{flake.accessor, canonPath(flake.flakePath + "/flake.lock")};
|
||||||
|
return lockFilePath.pathExists()
|
||||||
|
? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath))
|
||||||
|
: LockFile();
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute an in-memory lock file for the specified top-level flake,
|
/* Compute an in-memory lock file for the specified top-level flake,
|
||||||
and optionally write it to file, if the flake is writable. */
|
and optionally write it to file, if the flake is writable. */
|
||||||
LockedFlake lockFlake(
|
LockedFlake lockFlake(
|
||||||
|
@ -319,19 +343,16 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
|
auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
|
||||||
|
|
||||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
|
auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});
|
||||||
|
|
||||||
if (lockFlags.applyNixConfig) {
|
if (lockFlags.applyNixConfig) {
|
||||||
flake.config.apply();
|
flake.config.apply();
|
||||||
state.store->setOptions();
|
state.store->setOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// FIXME: symlink attack
|
auto oldLockFile = readLockFile(flake);
|
||||||
auto oldLockFile = LockFile::read(
|
|
||||||
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock");
|
|
||||||
|
|
||||||
debug("old lock file: %s", oldLockFile);
|
debug("old lock file: %s", oldLockFile);
|
||||||
|
|
||||||
|
@ -545,8 +566,7 @@ LockedFlake lockFlake(
|
||||||
inputFlake.inputs, childNode, inputPath,
|
inputFlake.inputs, childNode, inputPath,
|
||||||
oldLock
|
oldLock
|
||||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||||
: LockFile::read(
|
: readLockFile(inputFlake).root,
|
||||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
|
||||||
oldLock ? lockRootPath : inputPath, localPath, false);
|
oldLock ? lockRootPath : inputPath, localPath, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,12 +585,9 @@ LockedFlake lockFlake(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bring in the current ref for relative path resolution if we have it
|
|
||||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
|
||||||
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
flake.inputs, newLockFile.root, {},
|
flake.inputs, newLockFile.root, {},
|
||||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false);
|
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, flake.flakePath, false);
|
||||||
|
|
||||||
for (auto & i : lockFlags.inputOverrides)
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
if (!overridesUsed.count(i.first))
|
if (!overridesUsed.count(i.first))
|
||||||
|
@ -670,9 +687,6 @@ LockedFlake lockFlake(
|
||||||
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
|
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
throw UnimplementedError("lockFlake");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void callFlake(EvalState & state,
|
void callFlake(EvalState & state,
|
||||||
|
@ -687,17 +701,13 @@ void callFlake(EvalState & state,
|
||||||
|
|
||||||
vLocks->mkString(lockedFlake.lockFile.to_string());
|
vLocks->mkString(lockedFlake.lockFile.to_string());
|
||||||
|
|
||||||
#if 0
|
|
||||||
emitTreeAttrs(
|
emitTreeAttrs(
|
||||||
state,
|
state,
|
||||||
//*lockedFlake.flake.sourceInfo,
|
{lockedFlake.flake.accessor, lockedFlake.flake.flakePath},
|
||||||
lockedFlake.flake.lockedRef.input,
|
lockedFlake.flake.lockedRef.input,
|
||||||
*vRootSrc,
|
*vRootSrc,
|
||||||
false,
|
false,
|
||||||
lockedFlake.flake.forceDirty);
|
lockedFlake.flake.forceDirty);
|
||||||
#endif
|
|
||||||
|
|
||||||
throw UnimplementedError("callFlake");
|
|
||||||
|
|
||||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ struct Flake
|
||||||
FlakeRef originalRef; // the original flake specification (by the user)
|
FlakeRef originalRef; // the original flake specification (by the user)
|
||||||
FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake
|
FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake
|
||||||
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
||||||
|
ref<InputAccessor> accessor;
|
||||||
|
Path flakePath;
|
||||||
bool forceDirty = false; // pretend that 'lockedRef' is dirty
|
bool forceDirty = false; // pretend that 'lockedRef' is dirty
|
||||||
std::optional<std::string> description;
|
std::optional<std::string> description;
|
||||||
FlakeInputs inputs;
|
FlakeInputs inputs;
|
||||||
|
|
|
@ -34,7 +34,7 @@ typedef std::string FlakeId;
|
||||||
|
|
||||||
struct FlakeRef
|
struct FlakeRef
|
||||||
{
|
{
|
||||||
/* fetcher-specific representation of the input, sufficient to
|
/* Fetcher-specific representation of the input, sufficient to
|
||||||
perform the fetch operation. */
|
perform the fetch operation. */
|
||||||
fetchers::Input input;
|
fetchers::Input input;
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ std::shared_ptr<Node> LockFile::findInput(const InputPath & path)
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
LockFile::LockFile(const nlohmann::json & json, const Path & path)
|
LockFile::LockFile(const nlohmann::json & json, std::string_view path)
|
||||||
{
|
{
|
||||||
auto version = json.value("version", 0);
|
auto version = json.value("version", 0);
|
||||||
if (version < 5 || version > 7)
|
if (version < 5 || version > 7)
|
||||||
|
@ -116,6 +116,11 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
|
||||||
// a bit since we don't need to worry about cycles.
|
// a bit since we don't need to worry about cycles.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LockFile::LockFile(std::string_view contents, std::string_view path)
|
||||||
|
: LockFile(nlohmann::json::parse(contents), path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json LockFile::toJSON() const
|
nlohmann::json LockFile::toJSON() const
|
||||||
{
|
{
|
||||||
nlohmann::json nodes;
|
nlohmann::json nodes;
|
||||||
|
@ -183,12 +188,6 @@ std::string LockFile::to_string() const
|
||||||
return toJSON().dump(2);
|
return toJSON().dump(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LockFile LockFile::read(const Path & path)
|
|
||||||
{
|
|
||||||
if (!pathExists(path)) return LockFile();
|
|
||||||
return LockFile(nlohmann::json::parse(readFile(path)), path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
||||||
{
|
{
|
||||||
stream << lockFile.toJSON().dump(2);
|
stream << lockFile.toJSON().dump(2);
|
||||||
|
|
|
@ -50,7 +50,8 @@ struct LockFile
|
||||||
std::shared_ptr<Node> root = std::make_shared<Node>();
|
std::shared_ptr<Node> root = std::make_shared<Node>();
|
||||||
|
|
||||||
LockFile() {};
|
LockFile() {};
|
||||||
LockFile(const nlohmann::json & json, const Path & path);
|
LockFile(const nlohmann::json & json, std::string_view path);
|
||||||
|
LockFile(std::string_view contents, std::string_view path);
|
||||||
|
|
||||||
nlohmann::json toJSON() const;
|
nlohmann::json toJSON() const;
|
||||||
|
|
||||||
|
|
|
@ -716,7 +716,7 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path)
|
||||||
Expr * EvalState::parseExprFromFile(const SourcePath & path, StaticEnv & staticEnv)
|
Expr * EvalState::parseExprFromFile(const SourcePath & path, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
auto packed = packPath(path);
|
auto packed = packPath(path);
|
||||||
auto buffer = path.accessor->readFile(path.path);
|
auto buffer = path.readFile();
|
||||||
// readFile hopefully have left some extra space for terminators
|
// readFile hopefully have left some extra space for terminators
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
return parse(buffer.data(), buffer.size(), foFile, packed, dirOf(packed), staticEnv);
|
return parse(buffer.data(), buffer.size(), foFile, packed, dirOf(packed), staticEnv);
|
||||||
|
|
|
@ -1394,7 +1394,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
|
||||||
auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
|
auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
v.mkBool(path.accessor->pathExists(path.path));
|
v.mkBool(path.pathExists());
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
/* Don't give away info from errors while canonicalising
|
/* Don't give away info from errors while canonicalising
|
||||||
‘path’ in restricted mode. */
|
‘path’ in restricted mode. */
|
||||||
|
@ -1459,7 +1459,7 @@ static RegisterPrimOp primop_dirOf({
|
||||||
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
auto path = realisePath(state, pos, *args[0]);
|
auto path = realisePath(state, pos, *args[0]);
|
||||||
auto s = path.accessor->readFile(path.path);
|
auto s = path.readFile();
|
||||||
if (s.find((char) 0) != std::string::npos)
|
if (s.find((char) 0) != std::string::npos)
|
||||||
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
||||||
auto refs =
|
auto refs =
|
||||||
|
@ -1547,7 +1547,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
auto path = realisePath(state, pos, *args[1]);
|
auto path = realisePath(state, pos, *args[1]);
|
||||||
|
|
||||||
// FIXME: state.toRealPath(path, context)
|
// FIXME: state.toRealPath(path, context)
|
||||||
v.mkString(hashString(*ht, path.accessor->readFile(path.path)).to_string(Base16, false));
|
v.mkString(hashString(*ht, path.readFile()).to_string(Base16, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_hashFile({
|
static RegisterPrimOp primop_hashFile({
|
||||||
|
|
|
@ -67,6 +67,12 @@ struct SourcePath
|
||||||
Path path;
|
Path path;
|
||||||
|
|
||||||
std::string_view baseName() const;
|
std::string_view baseName() const;
|
||||||
|
|
||||||
|
std::string readFile() const
|
||||||
|
{ return accessor->readFile(path); }
|
||||||
|
|
||||||
|
bool pathExists() const
|
||||||
|
{ return accessor->pathExists(path); }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
||||||
|
|
Loading…
Reference in a new issue