From b9d64f931893120834fa54ebf084764d2e22ba33 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 1 Feb 2020 23:33:44 +0100 Subject: [PATCH] Record TreeInfo in the lock file Necessary for #3253. --- src/libexpr/flake/flake.cc | 26 +++++++++---- src/libexpr/flake/lockfile.cc | 59 ++++++++++++++++++++++++++++-- src/libexpr/flake/lockfile.hh | 9 +++-- src/libstore/fetchers/tree-info.hh | 11 +++++- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 099cf4c58..59728e38d 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -478,7 +478,7 @@ LockedFlake lockFlake( lockFlags.useRegistries, flakeCache); newLocks.inputs.insert_or_assign(id, - LockedInput(inputFlake.resolvedRef, inputFlake.originalRef, inputFlake.sourceInfo->info.narHash)); + LockedInput(inputFlake.resolvedRef, inputFlake.originalRef, inputFlake.sourceInfo->info)); /* Recursively process the inputs of this flake. Also, unless we already have this @@ -497,7 +497,7 @@ LockedFlake lockFlake( auto [sourceInfo, resolvedRef] = getNonFlake(state, input.ref, lockFlags.useRegistries, flakeCache); newLocks.inputs.insert_or_assign(id, - LockedInput(resolvedRef, input.ref, sourceInfo.info.narHash)); + LockedInput(resolvedRef, input.ref, sourceInfo.info)); } } } @@ -604,9 +604,14 @@ static void prim_callFlake(EvalState & state, const Pos & pos, Value * * args, V if (lazyInput->isFlake) { auto flake = getFlake(state, lazyInput->lockedInput.ref, false); - if (flake.sourceInfo->info.narHash != lazyInput->lockedInput.narHash) - throw Error("the content hash of flake '%s' doesn't match the hash recorded in the referring lockfile", - lazyInput->lockedInput.ref); + if (flake.sourceInfo->info.narHash != lazyInput->lockedInput.info.narHash) + throw Error("the content hash of flake '%s' (%s) doesn't match the hash recorded in the referring lock file (%s)", + lazyInput->lockedInput.ref, + flake.sourceInfo->info.narHash.to_string(SRI), + lazyInput->lockedInput.info.narHash.to_string(SRI)); + + // FIXME: check all the other attributes in lockedInput.info + // once we've dropped support for lock file version 4. assert(flake.sourceInfo->storePath == lazyInput->lockedInput.computeStorePath(*state.store)); @@ -615,9 +620,14 @@ static void prim_callFlake(EvalState & state, const Pos & pos, Value * * args, V FlakeCache flakeCache; auto [sourceInfo, resolvedRef] = getNonFlake(state, lazyInput->lockedInput.ref, false, flakeCache); - if (sourceInfo.info.narHash != lazyInput->lockedInput.narHash) - throw Error("the content hash of repository '%s' doesn't match the hash recorded in the referring lockfile", - lazyInput->lockedInput.ref); + if (sourceInfo.info.narHash != lazyInput->lockedInput.info.narHash) + throw Error("the content hash of repository '%s' (%s) doesn't match the hash recorded in the referring lock file (%s)", + lazyInput->lockedInput.ref, + sourceInfo.info.narHash.to_string(SRI), + lazyInput->lockedInput.info.narHash.to_string(SRI)); + + // FIXME: check all the other attributes in lockedInput.info + // once we've dropped support for lock file version 4. assert(sourceInfo.storePath == lazyInput->lockedInput.computeStorePath(*state.store)); diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index 60fb914f9..6773b2844 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -44,28 +44,81 @@ FlakeRef getFlakeRef( throw Error("attribute '%s' missing in lock file", version4Attr); } +static TreeInfo parseTreeInfo(const nlohmann::json & json) +{ + TreeInfo info; + + auto i = json.find("info"); + if (i != json.end()) { + const nlohmann::json & i2(*i); + + auto j = i2.find("narHash"); + if (j != i2.end()) + info.narHash = Hash((std::string) *j); + else + throw Error("attribute 'narHash' missing in lock file"); + + j = i2.find("rev"); + if (j != i2.end()) + info.rev = Hash((std::string) *j, htSHA1); + + j = i2.find("revCount"); + if (j != i2.end()) + info.revCount = *j; + + j = i2.find("lastModified"); + if (j != i2.end()) + info.lastModified = *j; + + return info; + } + + i = json.find("narHash"); + if (i != json.end()) { + info.narHash = Hash((std::string) *i); + return info; + } + + throw Error("attribute 'info' missing in lock file"); +} + LockedInput::LockedInput(const nlohmann::json & json) : LockedInputs(json) , ref(getFlakeRef(json, "url", "uri", "resolvedRef")) , originalRef(getFlakeRef(json, "originalUrl", "originalUri", "originalRef")) - , narHash(Hash((std::string) json["narHash"])) + , info(parseTreeInfo(json)) { if (!ref.isImmutable()) throw Error("lockfile contains mutable flakeref '%s'", ref); } +static nlohmann::json treeInfoToJson(const TreeInfo & info) +{ + nlohmann::json json; + assert(info.narHash); + json["narHash"] = info.narHash.to_string(SRI); + if (info.rev) + json["rev"] = info.rev->gitRev(); + if (info.revCount) + json["revCount"] = *info.revCount; + if (info.lastModified) + json["lastModified"] = *info.lastModified; + return json; +} + nlohmann::json LockedInput::toJson() const { auto json = LockedInputs::toJson(); json["originalRef"] = fetchers::attrsToJson(originalRef.toAttrs()); json["resolvedRef"] = fetchers::attrsToJson(ref.toAttrs()); - json["narHash"] = narHash.to_string(SRI); // FIXME + json["info"] = treeInfoToJson(info); return json; } StorePath LockedInput::computeStorePath(Store & store) const { - return store.makeFixedOutputPath(true, narHash, "source"); + assert(info.narHash); + return store.makeFixedOutputPath(true, info.narHash, "source"); } LockedInputs::LockedInputs(const nlohmann::json & json) diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh index a9710524b..d1cbf7c06 100644 --- a/src/libexpr/flake/lockfile.hh +++ b/src/libexpr/flake/lockfile.hh @@ -36,10 +36,10 @@ struct LockedInputs struct LockedInput : LockedInputs { FlakeRef ref, originalRef; - Hash narHash; + TreeInfo info; - LockedInput(const FlakeRef & ref, const FlakeRef & originalRef, const Hash & narHash) - : ref(ref), originalRef(originalRef), narHash(narHash) + LockedInput(const FlakeRef & ref, const FlakeRef & originalRef, const TreeInfo & info) + : ref(ref), originalRef(originalRef), info(info) { } LockedInput(const nlohmann::json & json); @@ -48,7 +48,8 @@ struct LockedInput : LockedInputs { return ref == other.ref - && narHash == other.narHash + && originalRef == other.originalRef + && info == other.info && inputs == other.inputs; } diff --git a/src/libstore/fetchers/tree-info.hh b/src/libstore/fetchers/tree-info.hh index 30d4f3d6b..61500facb 100644 --- a/src/libstore/fetchers/tree-info.hh +++ b/src/libstore/fetchers/tree-info.hh @@ -5,9 +5,18 @@ namespace nix { struct TreeInfo { Hash narHash; - std::optional rev; + std::optional rev; // FIXME: remove std::optional revCount; std::optional lastModified; + + bool operator ==(const TreeInfo & other) const + { + return + narHash == other.narHash + && rev == other.rev + && revCount == other.revCount + && lastModified == other.lastModified; + } }; }