diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 0a4b7d114..14837de92 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -94,7 +94,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s) if (isUri(s)) { auto storePath = fetchers::downloadTarball( state.store, resolveUri(s), "source", false).first; - auto accessor = makeFSInputAccessor(CanonPath(state.store->toRealPath(storePath))); + auto accessor = makeStorePathAccessor(state.store, storePath); state.registerAccessor(accessor); return {accessor, CanonPath::root}; } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 71228743b..4d69a5d75 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -791,7 +791,7 @@ SourcePath EvalState::findFile(SearchPath & searchPath, const std::string_view p try { auto storePath = fetchers::downloadTarball( store, resolveUri(elem.second), "source", false).first; - auto accessor = makeFSInputAccessor(CanonPath(store->toRealPath(storePath))); + auto accessor = makeStorePathAccessor(store, storePath); registerAccessor(accessor); res.emplace(SourcePath {accessor, CanonPath::root}); } catch (FileTransferError & e) { diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 66063a324..5b3dde436 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -115,32 +115,40 @@ bool Input::contains(const Input & other) const std::pair Input::fetchToStore(ref store) const { - if (!scheme) - throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); - auto [storePath, input] = [&]() -> std::pair { try { - return scheme->fetchToStore(store, *this); + auto [accessor, input2] = getAccessor(store); + + // FIXME: add an optimisation for the case where the + // accessor is an FSInputAccessor pointing to a store + // path. + auto source = sinkToSource([&, accessor{accessor}](Sink & sink) { + accessor->dumpPath(CanonPath::root, sink); + }); + + auto storePath = store->addToStoreFromDump(*source, input2.getName()); + + return {storePath, input2}; } catch (Error & e) { e.addTrace({}, "while fetching the input '%s'", to_string()); throw; } }(); - checkLocked(*store, storePath, input); - return {std::move(storePath), input}; } -void Input::checkLocked(Store & store, const StorePath & storePath, Input & input) const +void Input::checkLocks(Input & input) const { + #if 0 auto narHash = store.queryPathInfo(storePath)->narHash; input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true)); + #endif if (auto prevNarHash = getNarHash()) { - if (narHash != *prevNarHash) - throw Error((unsigned int) 102, "NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'", - to_string(), store.printStorePath(storePath), prevNarHash->to_string(SRI, true), narHash.to_string(SRI, true)); + if (input.getNarHash() != prevNarHash) + throw Error((unsigned int) 102, "NAR hash mismatch in input '%s', expected '%s'", + to_string(), prevNarHash->to_string(SRI, true)); } if (auto prevLastModified = getLastModified()) { @@ -155,9 +163,12 @@ void Input::checkLocked(Store & store, const StorePath & storePath, Input & inpu input.to_string(), *prevRevCount); } + // FIXME + #if 0 input.locked = true; assert(input.hasAllInfo()); + #endif } std::pair, Input> Input::getAccessor(ref store) const @@ -166,7 +177,9 @@ std::pair, Input> Input::getAccessor(ref store) const throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); try { - return scheme->getAccessor(store, *this); + auto [accessor, final] = scheme->getAccessor(store, *this); + checkLocks(final); + return {accessor, std::move(final)}; } catch (Error & e) { e.addTrace({}, "while fetching the input '%s'", to_string()); throw; @@ -262,7 +275,7 @@ std::optional Input::getFingerprint(ref store) const return scheme->getFingerprint(store, *this); } -ParsedURL InputScheme::toURL(const Input & input) +ParsedURL InputScheme::toURL(const Input & input) const { throw Error("don't know how to convert input '%s' to a URL", attrsToJSON(input.attrs)); } @@ -270,7 +283,7 @@ ParsedURL InputScheme::toURL(const Input & input) Input InputScheme::applyOverrides( const Input & input, std::optional ref, - std::optional rev) + std::optional rev) const { if (ref) throw Error("don't know how to set branch/tag name of input '%s' to '%s'", input.to_string(), *ref); @@ -288,31 +301,9 @@ void InputScheme::putFile( throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path); } -void InputScheme::clone(const Input & input, const Path & destDir) +void InputScheme::clone(const Input & input, const Path & destDir) const { throw Error("do not know how to clone input '%s'", input.to_string()); } -std::pair InputScheme::fetchToStore(ref store, const Input & input) -{ - auto [accessor, input2] = getAccessor(store, input); - - auto source = sinkToSource([&, accessor{accessor}](Sink & sink) { - accessor->dumpPath(CanonPath::root, sink); - }); - - auto storePath = store->addToStoreFromDump(*source, "source"); - - return {storePath, input2}; -} - -std::pair, Input> InputScheme::getAccessor(ref store, const Input & input) -{ - auto [storePath, input2] = fetchToStore(store, input); - - input.checkLocked(*store, storePath, input2); - - return {makeFSInputAccessor(CanonPath(store->toRealPath(storePath))), input2}; -} - } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 97bc4c20f..2a03dcaad 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -100,7 +100,7 @@ public: private: - void checkLocked(Store & store, const StorePath & storePath, Input & input) const; + void checkLocks(Input & input) const; }; /* The InputScheme represents a type of fetcher. Each fetcher @@ -116,20 +116,20 @@ struct InputScheme virtual ~InputScheme() { } - virtual std::optional inputFromURL(const ParsedURL & url) = 0; + virtual std::optional inputFromURL(const ParsedURL & url) const = 0; - virtual std::optional inputFromAttrs(const Attrs & attrs) = 0; + virtual std::optional inputFromAttrs(const Attrs & attrs) const = 0; - virtual ParsedURL toURL(const Input & input); + virtual ParsedURL toURL(const Input & input) const; - virtual bool hasAllInfo(const Input & input) = 0; + virtual bool hasAllInfo(const Input & input) const = 0; virtual Input applyOverrides( const Input & input, std::optional ref, - std::optional rev); + std::optional rev) const; - virtual void clone(const Input & input, const Path & destDir); + virtual void clone(const Input & input, const Path & destDir) const; virtual void putFile( const Input & input, @@ -137,13 +137,7 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; - /* Note: the default implementations of fetchToStore() and - getAccessor() are defined using the other, so implementations - have to override at least one. */ - - virtual std::pair fetchToStore(ref store, const Input & input); - - virtual std::pair, Input> getAccessor(ref store, const Input & input); + virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; virtual std::optional isRelative(const Input & input) const { return std::nullopt; } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index f1cbbdae3..2218f594b 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -140,7 +140,7 @@ bool isNotDotGitDirectory(const Path & path) struct GitInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (url.scheme != "git" && url.scheme != "git+http" && @@ -169,7 +169,7 @@ struct GitInputScheme : InputScheme return inputFromAttrs(attrs); } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { if (maybeGetStrAttr(attrs, "type") != "git") return {}; @@ -192,7 +192,7 @@ struct GitInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { auto url = parseURL(getStrAttr(input.attrs, "url")); if (url.scheme != "git") url.scheme = "git+" + url.scheme; @@ -203,7 +203,7 @@ struct GitInputScheme : InputScheme return url; } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { bool maybeDirty = !input.getRef(); bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false); @@ -215,7 +215,7 @@ struct GitInputScheme : InputScheme Input applyOverrides( const Input & input, std::optional ref, - std::optional rev) override + std::optional rev) const override { auto res(input); if (rev) res.attrs.insert_or_assign("rev", rev->gitRev()); @@ -225,7 +225,7 @@ struct GitInputScheme : InputScheme return res; } - void clone(const Input & input, const Path & destDir) override + void clone(const Input & input, const Path & destDir) const override { auto repoInfo = getRepoInfo(input); @@ -394,7 +394,7 @@ struct GitInputScheme : InputScheme return repoInfo; } - std::set listFiles(const RepoInfo & repoInfo) + std::set listFiles(const RepoInfo & repoInfo) const { auto gitOpts = Strings({ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "ls-files", "-z" }); if (repoInfo.submodules) @@ -409,14 +409,14 @@ struct GitInputScheme : InputScheme return res; } - void updateRev(Input & input, const RepoInfo & repoInfo, const std::string & ref) + void updateRev(Input & input, const RepoInfo & repoInfo, const std::string & ref) const { if (!input.getRev()) input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(runProgram("git", true, { "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "rev-parse", ref })), htSHA1).gitRev()); } - uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const std::string & ref) + uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const std::string & ref) const { return repoInfo.hasHead @@ -426,7 +426,7 @@ struct GitInputScheme : InputScheme : 0; } - uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) + uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { // FIXME: cache this. return @@ -437,7 +437,7 @@ struct GitInputScheme : InputScheme : 0; } - std::string getDefaultRef(const RepoInfo & repoInfo) + std::string getDefaultRef(const RepoInfo & repoInfo) const { auto head = repoInfo.isLocal ? readHead(repoInfo.url) @@ -449,11 +449,14 @@ struct GitInputScheme : InputScheme return *head; } - std::pair fetchToStore(ref store, const Input & _input) override + StorePath fetchToStore( + ref store, + RepoInfo & repoInfo, + Input & input) const { - Input input(_input); + assert(!repoInfo.isDirty); - auto repoInfo = getRepoInfo(input); + auto origRev = input.getRev(); std::string name = input.getName(); @@ -466,15 +469,21 @@ struct GitInputScheme : InputScheme }); }; - auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath) - -> std::pair + auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath { assert(input.getRev()); - assert(!_input.getRev() || _input.getRev() == input.getRev()); + assert(!origRev || origRev == input.getRev()); if (!repoInfo.shallow) input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified")); - return {std::move(storePath), input}; + + // FIXME: remove? + //input.attrs.erase("narHash"); + auto narHash = store->queryPathInfo(storePath)->narHash; + input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true)); + input.locked = true; + + return storePath; }; if (input.getRev()) { @@ -482,9 +491,6 @@ struct GitInputScheme : InputScheme return makeResult(res->first, std::move(res->second)); } - if (repoInfo.isDirty) - return fetchFromWorkdir(store, repoInfo, std::move(input)); - auto originalRef = input.getRef(); auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo); input.attrs.insert_or_assign("ref", ref); @@ -674,7 +680,7 @@ struct GitInputScheme : InputScheme infoAttrs.insert_or_assign("revCount", getRevCount(repoInfo, repoDir, rev)); - if (!_input.getRev()) + if (!origRev) getCache()->add( store, unlockedAttrs, @@ -692,45 +698,27 @@ struct GitInputScheme : InputScheme return makeResult(infoAttrs, std::move(storePath)); } - std::pair fetchFromWorkdir( - ref store, - const RepoInfo & repoInfo, - Input input) - { - /* This is an unclean working tree. So copy all tracked - files. */ - repoInfo.checkDirty(); - - auto files = listFiles(repoInfo); - - CanonPath repoDir(repoInfo.url); - - PathFilter filter = [&](const Path & p) -> bool { - return CanonPath(p).removePrefix(repoDir).isAllowed(files); - }; - - auto storePath = store->addToStore(input.getName(), repoInfo.url, FileIngestionMethod::Recursive, htSHA256, filter); - - // FIXME: maybe we should use the timestamp of the last - // modified dirty file? - input.attrs.insert_or_assign( - "lastModified", - getLastModified(repoInfo, repoInfo.url, "HEAD")); - - return {std::move(storePath), input}; - } - - std::pair, Input> getAccessor(ref store, const Input & _input) override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); auto repoInfo = getRepoInfo(input); + auto makeNotAllowedError = [url{repoInfo.url}](const CanonPath & path) -> RestrictedPathError + { + if (nix::pathExists(path.abs())) + return RestrictedPathError("access to path '%s' is forbidden because it is not under Git control; maybe you should 'git add' it to the repository '%s'?", path, url); + else + return RestrictedPathError("path '%s' does not exist in Git repository '%s'", path, url); + }; + /* Unless we're using the working tree, copy the tree into the Nix store. TODO: We could have an accessor for fetching files from the Git repository directly. */ - if (input.getRef() || input.getRev() || !repoInfo.isLocal) - return InputScheme::getAccessor(store, input); + if (input.getRef() || input.getRev() || !repoInfo.isLocal) { + auto storePath = fetchToStore(store, repoInfo, input); + return {makeStorePathAccessor(store, storePath, std::move(makeNotAllowedError)), input}; + } repoInfo.checkDirty(); @@ -753,14 +741,6 @@ struct GitInputScheme : InputScheme "lastModified", getLastModified(repoInfo, repoInfo.url, ref)); - auto makeNotAllowedError = [url{repoInfo.url}](const CanonPath & path) -> RestrictedPathError - { - if (nix::pathExists(path.abs())) - return RestrictedPathError("access to path '%s' is forbidden because it is not under Git control; maybe you should 'git add' it to the repository '%s'?", path, url); - else - return RestrictedPathError("path '%s' does not exist in Git repository '%s'", path, url); - }; - return {makeFSInputAccessor(CanonPath(repoInfo.url), listFiles(repoInfo), std::move(makeNotAllowedError)), input}; } }; diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 5409594f7..4005a1f48 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -26,11 +26,11 @@ std::regex hostRegex(hostRegexS, std::regex::ECMAScript); struct GitArchiveInputScheme : InputScheme { - virtual std::string type() = 0; + virtual std::string type() const = 0; virtual std::optional> accessHeaderFromToken(const std::string & token) const = 0; - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (url.scheme != type()) return {}; @@ -100,7 +100,7 @@ struct GitArchiveInputScheme : InputScheme return input; } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { if (maybeGetStrAttr(attrs, "type") != type()) return {}; @@ -116,7 +116,7 @@ struct GitArchiveInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { auto owner = getStrAttr(input.attrs, "owner"); auto repo = getStrAttr(input.attrs, "repo"); @@ -132,7 +132,7 @@ struct GitArchiveInputScheme : InputScheme }; } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { return input.getRev() && true; // FIXME @@ -142,7 +142,7 @@ struct GitArchiveInputScheme : InputScheme Input applyOverrides( const Input & _input, std::optional ref, - std::optional rev) override + std::optional rev) const override { auto input(_input); if (rev && ref) @@ -185,7 +185,7 @@ struct GitArchiveInputScheme : InputScheme virtual DownloadUrl getDownloadUrl(const Input & input) const = 0; - std::pair downloadArchive(ref store, Input input) + std::pair downloadArchive(ref store, Input input) const { if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD"); @@ -228,7 +228,7 @@ struct GitArchiveInputScheme : InputScheme return {res.storePath, input}; } - std::pair, Input> getAccessor(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & input) const override { auto [storePath, input2] = downloadArchive(store, input); @@ -242,7 +242,7 @@ struct GitArchiveInputScheme : InputScheme struct GitHubInputScheme : GitArchiveInputScheme { - std::string type() override { return "github"; } + std::string type() const override { return "github"; } std::optional> accessHeaderFromToken(const std::string & token) const override { @@ -291,7 +291,7 @@ struct GitHubInputScheme : GitArchiveInputScheme return DownloadUrl { url, headers }; } - void clone(const Input & input, const Path & destDir) override + void clone(const Input & input, const Path & destDir) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("github.com"); Input::fromURL(fmt("git+https://%s/%s/%s.git", @@ -303,7 +303,7 @@ struct GitHubInputScheme : GitArchiveInputScheme struct GitLabInputScheme : GitArchiveInputScheme { - std::string type() override { return "gitlab"; } + std::string type() const override { return "gitlab"; } std::optional> accessHeaderFromToken(const std::string & token) const override { @@ -358,7 +358,7 @@ struct GitLabInputScheme : GitArchiveInputScheme return DownloadUrl { url, headers }; } - void clone(const Input & input, const Path & destDir) override + void clone(const Input & input, const Path & destDir) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // FIXME: get username somewhere @@ -371,7 +371,7 @@ struct GitLabInputScheme : GitArchiveInputScheme struct SourceHutInputScheme : GitArchiveInputScheme { - std::string type() override { return "sourcehut"; } + std::string type() const override { return "sourcehut"; } std::optional> accessHeaderFromToken(const std::string & token) const override { @@ -445,7 +445,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme return DownloadUrl { url, headers }; } - void clone(const Input & input, const Path & destDir) override + void clone(const Input & input, const Path & destDir) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); Input::fromURL(fmt("git+https://%s/%s/%s", diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 32ee6719d..bd4ecf320 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -7,7 +7,7 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); struct IndirectInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (url.scheme != "flake") return {}; @@ -50,7 +50,7 @@ struct IndirectInputScheme : InputScheme return input; } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { if (maybeGetStrAttr(attrs, "type") != "indirect") return {}; @@ -68,7 +68,7 @@ struct IndirectInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { ParsedURL url; url.scheme = "flake"; @@ -78,7 +78,7 @@ struct IndirectInputScheme : InputScheme return url; } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { return false; } @@ -86,7 +86,7 @@ struct IndirectInputScheme : InputScheme Input applyOverrides( const Input & _input, std::optional ref, - std::optional rev) override + std::optional rev) const override { auto input(_input); if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); @@ -94,7 +94,7 @@ struct IndirectInputScheme : InputScheme return input; } - std::pair fetchToStore(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & input) const override { throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); } diff --git a/src/libfetchers/input-accessor.cc b/src/libfetchers/input-accessor.cc index 55aecd839..5df447f01 100644 --- a/src/libfetchers/input-accessor.cc +++ b/src/libfetchers/input-accessor.cc @@ -1,5 +1,6 @@ #include "input-accessor.hh" #include "util.hh" +#include "store-api.hh" #include @@ -235,6 +236,14 @@ ref makeFSInputAccessor( return make_ref(root, std::move(allowedPaths), std::move(makeNotAllowedError)); } +ref makeStorePathAccessor( + ref store, + const StorePath & storePath, + MakeNotAllowedError && makeNotAllowedError) +{ + return makeFSInputAccessor(CanonPath(store->toRealPath(storePath)), {}, std::move(makeNotAllowedError)); +} + std::ostream & operator << (std::ostream & str, const SourcePath & path) { str << path.to_string(); diff --git a/src/libfetchers/input-accessor.hh b/src/libfetchers/input-accessor.hh index 28bf38bce..2c0be75ff 100644 --- a/src/libfetchers/input-accessor.hh +++ b/src/libfetchers/input-accessor.hh @@ -10,6 +10,8 @@ namespace nix { MakeError(RestrictedPathError, Error); struct SourcePath; +class StorePath; +class Store; struct InputAccessor : public std::enable_shared_from_this { @@ -91,6 +93,11 @@ ref makeFSInputAccessor( std::optional> && allowedPaths = {}, MakeNotAllowedError && makeNotAllowedError = {}); +ref makeStorePathAccessor( + ref store, + const StorePath & storePath, + MakeNotAllowedError && makeNotAllowedError = {}); + struct SourcePath; struct MemoryInputAccessor : InputAccessor diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index a10fc0a63..ccd504e15 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -43,7 +43,7 @@ static std::string runHg(const Strings & args, const std::optional struct MercurialInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (url.scheme != "hg+http" && url.scheme != "hg+https" && @@ -69,7 +69,7 @@ struct MercurialInputScheme : InputScheme return inputFromAttrs(attrs); } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { if (maybeGetStrAttr(attrs, "type") != "hg") return {}; @@ -89,7 +89,7 @@ struct MercurialInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { auto url = parseURL(getStrAttr(input.attrs, "url")); url.scheme = "hg+" + url.scheme; @@ -98,7 +98,7 @@ struct MercurialInputScheme : InputScheme return url; } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { // FIXME: ugly, need to distinguish between dirty and clean // default trees. @@ -108,7 +108,7 @@ struct MercurialInputScheme : InputScheme Input applyOverrides( const Input & input, std::optional ref, - std::optional rev) override + std::optional rev) const override { auto res(input); if (rev) res.attrs.insert_or_assign("rev", rev->gitRev()); @@ -148,9 +148,9 @@ struct MercurialInputScheme : InputScheme return {isLocal, isLocal ? url.path : url.base}; } - std::pair fetchToStore(ref store, const Input & _input) override + StorePath fetchToStore(ref store, Input & input) const { - Input input(_input); + auto origRev = input.getRev(); auto name = input.getName(); @@ -200,7 +200,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter); - return {std::move(storePath), input}; + return storePath; } } @@ -224,13 +224,13 @@ struct MercurialInputScheme : InputScheme }); }; - auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath) - -> std::pair + auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath { assert(input.getRev()); - assert(!_input.getRev() || _input.getRev() == input.getRev()); + assert(!origRev || origRev == input.getRev()); input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); - return {std::move(storePath), input}; + input.locked = true; + return storePath; }; if (input.getRev()) { @@ -310,7 +310,7 @@ struct MercurialInputScheme : InputScheme {"revCount", (uint64_t) revCount}, }); - if (!_input.getRev()) + if (!origRev) getCache()->add( store, unlockedAttrs, @@ -327,6 +327,15 @@ struct MercurialInputScheme : InputScheme return makeResult(infoAttrs, std::move(storePath)); } + + std::pair, Input> getAccessor(ref store, const Input & _input) const override + { + Input input(_input); + + auto storePath = fetchToStore(store, input); + + return {makeStorePathAccessor(store, storePath), input}; + } }; static auto rMercurialInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index ea056861b..4d3dad679 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -6,7 +6,7 @@ namespace nix::fetchers { struct PathInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (url.scheme != "path") return {}; @@ -32,7 +32,7 @@ struct PathInputScheme : InputScheme return input; } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { if (maybeGetStrAttr(attrs, "type") != "path") return {}; @@ -54,7 +54,7 @@ struct PathInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { auto query = attrsToQuery(input.attrs); query.erase("path"); @@ -75,7 +75,7 @@ struct PathInputScheme : InputScheme return CanonPath(path); } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { return true; } @@ -103,7 +103,7 @@ struct PathInputScheme : InputScheme throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); } - std::pair, Input> getAccessor(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & input) const override { auto absPath = getAbsPath(input); auto input2(input); diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index ab8f847dd..964a10124 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -185,7 +185,7 @@ struct CurlInputScheme : InputScheme virtual bool isValidURL(const ParsedURL & url) const = 0; - std::optional inputFromURL(const ParsedURL & url) override + std::optional inputFromURL(const ParsedURL & url) const override { if (!isValidURL(url)) return std::nullopt; @@ -203,7 +203,7 @@ struct CurlInputScheme : InputScheme return input; } - std::optional inputFromAttrs(const Attrs & attrs) override + std::optional inputFromAttrs(const Attrs & attrs) const override { auto type = maybeGetStrAttr(attrs, "type"); if (type != inputType()) return {}; @@ -220,7 +220,7 @@ struct CurlInputScheme : InputScheme return input; } - ParsedURL toURL(const Input & input) override + ParsedURL toURL(const Input & input) const override { auto url = parseURL(getStrAttr(input.attrs, "url")); // NAR hashes are preferred over file hashes since tar/zip @@ -230,7 +230,7 @@ struct CurlInputScheme : InputScheme return url; } - bool hasAllInfo(const Input & input) override + bool hasAllInfo(const Input & input) const override { return true; } @@ -250,10 +250,18 @@ struct FileInputScheme : CurlInputScheme : !hasTarballExtension(url.path)); } - std::pair fetchToStore(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { + auto input(_input); + auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false); - return {std::move(file.storePath), input}; + + // FIXME: remove? + auto narHash = store->queryPathInfo(file.storePath)->narHash; + input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true)); + input.locked = true; + + return {makeStorePathAccessor(store, file.storePath), input}; } }; @@ -271,12 +279,18 @@ struct TarballInputScheme : CurlInputScheme : hasTarballExtension(url.path)); } - std::pair fetchToStore(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { - return { - downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first, - input - }; + auto input(_input); + + auto storePath = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first; + + // FIXME: remove? + auto narHash = store->queryPathInfo(storePath)->narHash; + input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true)); + input.locked = true; + + return {makeStorePathAccessor(store, storePath), input}; } }; diff --git a/tests/fetchGit.sh b/tests/fetchGit.sh index 166bccfc7..108be4c02 100644 --- a/tests/fetchGit.sh +++ b/tests/fetchGit.sh @@ -120,6 +120,7 @@ git -C $repo commit -m 'Bla3' -a path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchGit file://$repo).outPath") [[ $path2 = $path4 ]] +status=0 nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; narHash = \"sha256-B5yIPHhEm0eysJKEsO7nqxprh9vcblFxpJG11gXJus1=\"; }).outPath" || status=$? [[ "$status" = "102" ]] @@ -223,4 +224,5 @@ rm -rf $repo/.git # should succeed for a repo without commits git init $repo +git -C $repo add hello # need to add at least one file to cause the root of the repo to be visible path10=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath")