From f5bdba4a15ed23bf6eb2050c8b280c4e67f4fbf3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 12 Nov 2025 21:08:27 +0100 Subject: [PATCH 1/2] Remove setting from Input This is more straightforward and not subject to undocumented memory safety restrictions. Also easier to test. (cherry picked from commit 292bd390afbe7f8d3014251faba4cef25b516373) --- src/libcmd/common-eval-args.cc | 7 +- src/libcmd/installables.cc | 1 + src/libcmd/repl.cc | 2 +- src/libexpr/paths.cc | 2 +- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 9 +- src/libfetchers-tests/git.cc | 2 +- src/libfetchers/builtin.cc | 2 +- src/libfetchers/fetchers.cc | 31 ++++--- src/libfetchers/git.cc | 39 ++++---- src/libfetchers/github.cc | 91 ++++++++++--------- .../include/nix/fetchers/fetchers.hh | 25 ++--- .../include/nix/fetchers/input-cache.hh | 4 +- .../include/nix/fetchers/registry.hh | 5 +- src/libfetchers/indirect.cc | 7 +- src/libfetchers/input-cache.cc | 10 +- src/libfetchers/mercurial.cc | 25 ++--- src/libfetchers/path.cc | 11 ++- src/libfetchers/registry.cc | 9 +- src/libfetchers/tarball.cc | 25 +++-- src/libflake/flake-primops.cc | 2 +- src/libflake/flake.cc | 13 ++- src/libflake/flakeref.cc | 10 +- src/libflake/include/nix/flake/flakeref.hh | 8 +- src/libflake/lockfile.cc | 4 +- src/nix/flake-prefetch-inputs.cc | 2 +- src/nix/flake.cc | 16 ++-- src/nix/profile.cc | 5 +- src/nix/registry.cc | 5 +- 29 files changed, 198 insertions(+), 176 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 890fe8337b3..d3a1df73312 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -28,7 +28,8 @@ EvalSettings evalSettings{ // FIXME `parseFlakeRef` should take a `std::string_view`. auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false); debug("fetching flake search path element '%s''", rest); - auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store); + auto [accessor, lockedRef] = + flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store); auto storePath = nix::fetchToStore( state.fetchSettings, *state.store, @@ -126,7 +127,7 @@ MixEvalArgs::MixEvalArgs() fetchers::Attrs extraAttrs; if (to.subdir != "") extraAttrs["dir"] = to.subdir; - fetchers::overrideRegistry(from.input, to.input, extraAttrs); + fetchers::overrideRegistry(fetchSettings, from.input, to.input, extraAttrs); }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { completeFlakeRef(completions, openStore(), prefix); @@ -181,7 +182,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas else if (hasPrefix(s, "flake:")) { auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); - auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store); + auto [accessor, lockedRef] = flakeRef.resolve(fetchSettings, state.store).lazyFetch(fetchSettings, state.store); auto storePath = nix::fetchToStore( state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName()); state.allowPath(storePath); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 37959431a18..aa51ec278d9 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -185,6 +185,7 @@ MixFlakeOptions::MixFlakeOptions() } overrideRegistry( + fetchSettings, fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}), input3->lockedRef.input, extraAttrs); diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 2a6582cd01b..ab5fc562f2c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -739,7 +739,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS) } auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true); - if (evalSettings.pureEval && !flakeRef.input.isLocked()) + if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings)) throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); Value v; diff --git a/src/libexpr/paths.cc b/src/libexpr/paths.cc index d5dd9518ae2..17189d507ca 100644 --- a/src/libexpr/paths.cc +++ b/src/libexpr/paths.cc @@ -100,7 +100,7 @@ StorePath EvalState::mountInput( storeFS->mount(CanonPath(store->printStorePath(storePath)), accessor); if (forceNarHash - || (requireLockable && (!settings.lazyTrees || !settings.lazyLocks || !input.isLocked()) + || (requireLockable && (!settings.lazyTrees || !settings.lazyLocks || !input.isLocked(fetchSettings)) && !input.getNarHash())) input.attrs.insert_or_assign("narHash", getNarHash()->to_string(HashFormat::SRI, true)); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index c700c181c21..942a07f89ab 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -81,7 +81,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); - auto [storePath, accessor, input2] = input.fetchToStore(state.store); + auto [storePath, accessor, input2] = input.fetchToStore(state.fetchSettings, state.store); auto attrs2 = state.buildBindings(8); state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath)); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 5d2d7319293..f1d9ea41916 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -82,7 +82,7 @@ struct FetchTreeParams static void fetchTree( EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{}) { - fetchers::Input input{state.fetchSettings}; + fetchers::Input input{}; NixStringContext context; std::optional type; auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree"; @@ -188,9 +188,9 @@ static void fetchTree( } if (!state.settings.pureEval && !input.isDirect()) - input = lookupInRegistries(state.store, input, fetchers::UseRegistries::Limited).first; + input = lookupInRegistries(state.fetchSettings, state.store, input, fetchers::UseRegistries::Limited).first; - if (state.settings.pureEval && !input.isLocked()) { + if (state.settings.pureEval && !input.isLocked(state.fetchSettings)) { if (input.getNarHash()) warn( "Input '%s' is unlocked (e.g. lacks a Git revision) but does have a NAR hash. " @@ -213,7 +213,8 @@ static void fetchTree( throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string()); } - auto cachedInput = state.inputCache->getAccessor(state.store, input, fetchers::UseRegistries::No); + auto cachedInput = + state.inputCache->getAccessor(state.fetchSettings, state.store, input, fetchers::UseRegistries::No); auto storePath = state.mountInput(cachedInput.lockedInput, input, cachedInput.accessor, true); diff --git a/src/libfetchers-tests/git.cc b/src/libfetchers-tests/git.cc index 4f0e0d97479..e8092b86cad 100644 --- a/src/libfetchers-tests/git.cc +++ b/src/libfetchers-tests/git.cc @@ -196,7 +196,7 @@ TEST_F(GitTest, submodulePeriodSupport) {"ref", "main"}, }); - auto [accessor, i] = input.getAccessor(store); + auto [accessor, i] = input.getAccessor(settings, store); ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n"); } diff --git a/src/libfetchers/builtin.cc b/src/libfetchers/builtin.cc index c1a912c25c6..a2df93939a6 100644 --- a/src/libfetchers/builtin.cc +++ b/src/libfetchers/builtin.cc @@ -48,7 +48,7 @@ static void builtinFetchTree(const BuiltinBuilderContext & ctx) Nix's daemon so we can use the real store? */ auto tmpStore = openStore(ctx.tmpDirInSandbox + "/nix"); - auto [accessor, lockedInput] = input.getAccessor(tmpStore); + auto [accessor, lockedInput] = input.getAccessor(myFetchSettings, tmpStore); auto source = sinkToSource([&](Sink & sink) { accessor->dumpPath(CanonPath::root, sink); }); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index f56a399f17e..5a987cd73ca 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -84,7 +84,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs) // but not all of them. Doing this is to support those other // operations which are supposed to be robust on // unknown/uninterpretable inputs. - Input input{settings}; + Input input; input.attrs = attrs; fixupInput(input); return input; @@ -160,9 +160,9 @@ bool Input::isDirect() const return !scheme || scheme->isDirect(*this); } -bool Input::isLocked() const +bool Input::isLocked(const Settings & settings) const { - return scheme && scheme->isLocked(*this); + return scheme && scheme->isLocked(settings, *this); } bool Input::isFinal() const @@ -199,15 +199,15 @@ bool Input::contains(const Input & other) const } // FIXME: remove -std::tuple, Input> Input::fetchToStore(ref store) const +std::tuple, Input> Input::fetchToStore(const Settings & settings, ref store) const { if (!scheme) throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); try { - auto [accessor, result] = getAccessorUnchecked(store); + auto [accessor, result] = getAccessorUnchecked(settings, store); - auto storePath = nix::fetchToStore(*settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName()); + auto storePath = nix::fetchToStore(settings, *store, SourcePath(accessor), FetchMode::Copy, result.getName()); auto narHash = store->queryPathInfo(storePath)->narHash; result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); @@ -296,10 +296,10 @@ void Input::checkLocks(Input specified, Input & result) } } -std::pair, Input> Input::getAccessor(ref store) const +std::pair, Input> Input::getAccessor(const Settings & settings, ref store) const { try { - auto [accessor, result] = getAccessorUnchecked(store); + auto [accessor, result] = getAccessorUnchecked(settings, store); result.attrs.insert_or_assign("__final", Explicit(true)); @@ -327,7 +327,7 @@ struct SubstitutedSourceAccessor : ForwardingSourceAccessor } }; -std::pair, Input> Input::getAccessorUnchecked(ref store) const +std::pair, Input> Input::getAccessorUnchecked(const Settings & settings, ref store) const { // FIXME: cache the accessor @@ -347,7 +347,7 @@ std::pair, Input> Input::getAccessorUnchecked(ref sto // can reuse the existing nar instead of copying the unpacked // input back into the store on every evaluation. if (accessor->fingerprint) { - settings->getCache()->upsert( + settings.getCache()->upsert( makeSourcePathToHashCacheKey(*accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"), {{"hash", store->queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true)}}); } @@ -369,7 +369,7 @@ std::pair, Input> Input::getAccessorUnchecked(ref sto } try { - auto [accessor, result] = scheme->getAccessor(store, *this); + auto [accessor, result] = scheme->getAccessor(settings, store, *this); if (!accessor->fingerprint) accessor->fingerprint = result.getFingerprint(store); @@ -405,10 +405,10 @@ Input Input::applyOverrides(std::optional ref, std::optional return scheme->applyOverrides(*this, ref, rev); } -void Input::clone(ref store, const std::filesystem::path & destDir) const +void Input::clone(const Settings & settings, ref store, const std::filesystem::path & destDir) const { assert(scheme); - scheme->clone(store, *this, destDir); + scheme->clone(settings, store, *this, destDir); } std::optional Input::getSourcePath() const @@ -521,12 +521,13 @@ void InputScheme::putFile( throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path); } -void InputScheme::clone(ref store, const Input & input, const std::filesystem::path & destDir) const +void InputScheme::clone( + const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) const { if (std::filesystem::exists(destDir)) throw Error("cannot clone into existing path %s", destDir); - auto [accessor, input2] = getAccessor(store, input); + auto [accessor, input2] = getAccessor(settings, store, input); Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to %s...", input2.to_string(), destDir)); diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 2e0af04d5d4..208aea74a6f 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -231,7 +231,7 @@ struct GitInputScheme : InputScheme if (auto ref = maybeGetStrAttr(attrs, "ref"); ref && !isLegalRefName(*ref)) throw BadURL("invalid Git branch/tag name '%s'", *ref); - Input input{settings}; + Input input{}; input.attrs = attrs; input.attrs["url"] = fixGitURL(getStrAttr(attrs, "url")).to_string(); getShallowAttr(input); @@ -282,7 +282,8 @@ struct GitInputScheme : InputScheme return res; } - void clone(ref store, const Input & input, const std::filesystem::path & destDir) const override + void clone(const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) + const override { auto repoInfo = getRepoInfo(input); @@ -627,7 +628,7 @@ struct GitInputScheme : InputScheme } std::pair, Input> - getAccessorFromCommit(ref store, RepoInfo & repoInfo, Input && input) const + getAccessorFromCommit(const Settings & settings, ref store, RepoInfo & repoInfo, Input && input) const { assert(!repoInfo.workdirInfo.isDirty); @@ -739,11 +740,11 @@ struct GitInputScheme : InputScheme Attrs infoAttrs({ {"rev", rev.gitRev()}, - {"lastModified", getLastModified(*input.settings, repoInfo, repoDir, rev)}, + {"lastModified", getLastModified(settings, repoInfo, repoDir, rev)}, }); if (!getShallowAttr(input)) - infoAttrs.insert_or_assign("revCount", getRevCount(*input.settings, repoInfo, repoDir, rev)); + infoAttrs.insert_or_assign("revCount", getRevCount(settings, repoInfo, repoDir, rev)); printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg()); @@ -786,8 +787,8 @@ struct GitInputScheme : InputScheme attrs.insert_or_assign("submodules", Explicit{true}); attrs.insert_or_assign("lfs", Explicit{smudgeLfs}); attrs.insert_or_assign("allRefs", Explicit{true}); - auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); - auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); + auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs)); + auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string(true) + "»"); mounts.insert_or_assign(submodule.path, submoduleAccessor); } @@ -807,7 +808,7 @@ struct GitInputScheme : InputScheme } std::pair, Input> - getAccessorFromWorkdir(ref store, RepoInfo & repoInfo, Input && input) const + getAccessorFromWorkdir(const Settings & settings, ref store, RepoInfo & repoInfo, Input && input) const { auto repoPath = repoInfo.getPath().value(); @@ -839,8 +840,8 @@ struct GitInputScheme : InputScheme // TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out // attrs.insert_or_assign("allRefs", Explicit{ true }); - auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); - auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); + auto submoduleInput = fetchers::Input::fromAttrs(settings, std::move(attrs)); + auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(settings, store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string(true) + "»"); /* If the submodule is dirty, mark this repo dirty as @@ -867,12 +868,12 @@ struct GitInputScheme : InputScheme input.attrs.insert_or_assign("rev", rev.gitRev()); if (!getShallowAttr(input)) { input.attrs.insert_or_assign( - "revCount", rev == nullRev ? 0 : getRevCount(*input.settings, repoInfo, repoPath, rev)); + "revCount", rev == nullRev ? 0 : getRevCount(settings, repoInfo, repoPath, rev)); } verifyCommit(input, repo); } else { - repoInfo.warnDirty(*input.settings); + repoInfo.warnDirty(settings); if (repoInfo.workdirInfo.headRev) { input.attrs.insert_or_assign("dirtyRev", repoInfo.workdirInfo.headRev->gitRev() + "-dirty"); @@ -884,14 +885,14 @@ struct GitInputScheme : InputScheme input.attrs.insert_or_assign( "lastModified", - repoInfo.workdirInfo.headRev - ? getLastModified(*input.settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev) - : 0); + repoInfo.workdirInfo.headRev ? getLastModified(settings, repoInfo, repoPath, *repoInfo.workdirInfo.headRev) + : 0); return {accessor, std::move(input)}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { Input input(_input); @@ -907,8 +908,8 @@ struct GitInputScheme : InputScheme } auto [accessor, final] = input.getRef() || input.getRev() || !repoInfo.getPath() - ? getAccessorFromCommit(store, repoInfo, std::move(input)) - : getAccessorFromWorkdir(store, repoInfo, std::move(input)); + ? getAccessorFromCommit(settings, store, repoInfo, std::move(input)) + : getAccessorFromWorkdir(settings, store, repoInfo, std::move(input)); return {accessor, std::move(final)}; } @@ -944,7 +945,7 @@ struct GitInputScheme : InputScheme } } - bool isLocked(const Input & input) const override + bool isLocked(const Settings & settings, const Input & input) const override { auto rev = input.getRev(); return rev && rev != nullRev; diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 6176a47f34b..2f43f71f617 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -92,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme if (ref && rev) throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev()); - Input input{settings}; + Input input{}; input.attrs.insert_or_assign("type", std::string{schemeName()}); input.attrs.insert_or_assign("owner", path[0]); input.attrs.insert_or_assign("repo", path[1]); @@ -129,7 +129,7 @@ struct GitArchiveInputScheme : InputScheme getStrAttr(attrs, "owner"); getStrAttr(attrs, "repo"); - Input input{settings}; + Input input{}; input.attrs = attrs; return input; } @@ -233,9 +233,9 @@ struct GitArchiveInputScheme : InputScheme std::optional treeHash; }; - virtual RefInfo getRevFromRef(nix::ref store, const Input & input) const = 0; + virtual RefInfo getRevFromRef(const Settings & settings, nix::ref store, const Input & input) const = 0; - virtual DownloadUrl getDownloadUrl(const Input & input) const = 0; + virtual DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const = 0; struct TarballInfo { @@ -243,7 +243,7 @@ struct GitArchiveInputScheme : InputScheme time_t lastModified; }; - std::pair downloadArchive(ref store, Input input) const + std::pair downloadArchive(const Settings & settings, ref store, Input input) const { if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD"); @@ -252,7 +252,7 @@ struct GitArchiveInputScheme : InputScheme auto rev = input.getRev(); if (!rev) { - auto refInfo = getRevFromRef(store, input); + auto refInfo = getRevFromRef(settings, store, input); rev = refInfo.rev; upstreamTreeHash = refInfo.treeHash; debug("HEAD revision for '%s' is %s", input.to_string(), refInfo.rev.gitRev()); @@ -261,7 +261,7 @@ struct GitArchiveInputScheme : InputScheme input.attrs.erase("ref"); input.attrs.insert_or_assign("rev", rev->gitRev()); - auto cache = input.settings->getCache(); + auto cache = settings.getCache(); Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}}; Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}}; @@ -270,7 +270,7 @@ struct GitArchiveInputScheme : InputScheme if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) { auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); - if (input.settings->getTarballCache()->hasObject(treeHash)) + if (settings.getTarballCache()->hasObject(treeHash)) return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}}; else debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev()); @@ -278,7 +278,7 @@ struct GitArchiveInputScheme : InputScheme } /* Stream the tarball into the tarball cache. */ - auto url = getDownloadUrl(input); + auto url = getDownloadUrl(settings, input); auto source = sinkToSource([&](Sink & sink) { FileTransferRequest req(url.url); @@ -290,7 +290,7 @@ struct GitArchiveInputScheme : InputScheme *logger, lvlInfo, actUnknown, fmt("unpacking '%s' into the Git cache", input.to_string())); TarArchive archive{*source}; - auto tarballCache = input.settings->getTarballCache(); + auto tarballCache = settings.getTarballCache(); auto parseSink = tarballCache->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); auto tree = parseSink->flush(); @@ -315,19 +315,20 @@ struct GitArchiveInputScheme : InputScheme return {std::move(input), tarballInfo}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { - auto [input, tarballInfo] = downloadArchive(store, _input); + auto [input, tarballInfo] = downloadArchive(settings, store, _input); #if 0 input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev()); #endif input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified)); - auto accessor = input.settings->getTarballCache()->getAccessor( - tarballInfo.treeHash, false, "«" + input.to_string(true) + "»"); + auto accessor = + settings.getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string(true) + "»"); - if (!input.settings->trustTarballsFromGitForges) + if (!settings.trustTarballsFromGitForges) // FIXME: computing the NAR hash here is wasteful if // copyInputToStore() is just going to hash/copy it as // well. @@ -337,14 +338,13 @@ struct GitArchiveInputScheme : InputScheme return {accessor, input}; } - bool isLocked(const Input & input) const override + bool isLocked(const Settings & settings, const Input & input) const override { /* Since we can't verify the integrity of the tarball from the Git revision alone, we also require a NAR hash for locking. FIXME: in the future, we may want to require a Git tree hash instead of a NAR hash. */ - return input.getRev().has_value() - && (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value()); + return input.getRev().has_value() && (settings.trustTarballsFromGitForges || input.getNarHash().has_value()); } std::optional getFingerprint(ref store, const Input & input) const override @@ -389,7 +389,7 @@ struct GitHubInputScheme : GitArchiveInputScheme return getStrAttr(input.attrs, "repo"); } - RefInfo getRevFromRef(nix::ref store, const Input & input) const override + RefInfo getRevFromRef(const Settings & settings, nix::ref store, const Input & input) const override { auto host = getHost(input); auto url = fmt( @@ -399,9 +399,9 @@ struct GitHubInputScheme : GitArchiveInputScheme getRepo(input), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); - auto downloadResult = downloadFile(store, *input.settings, url, "source", headers); + auto downloadResult = downloadFile(store, settings, url, "source", headers); auto json = nlohmann::json::parse(store->getFSAccessor(downloadResult.storePath)->readFile(CanonPath::root)); return RefInfo{ @@ -409,11 +409,11 @@ struct GitHubInputScheme : GitArchiveInputScheme .treeHash = Hash::parseAny(std::string{json["commit"]["tree"]["sha"]}, HashAlgorithm::SHA1)}; } - DownloadUrl getDownloadUrl(const Input & input) const override + DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override { auto host = getHost(input); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); // If we have no auth headers then we default to the public archive // urls so we do not run into rate limits. @@ -426,12 +426,13 @@ struct GitHubInputScheme : GitArchiveInputScheme return DownloadUrl{parseURL(url), headers}; } - void clone(ref store, const Input & input, const std::filesystem::path & destDir) const override + void clone(const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) + const override { auto host = getHost(input); - Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input))) + Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input))) .applyOverrides(input.getRef(), input.getRev()) - .clone(store, destDir); + .clone(settings, store, destDir); } }; @@ -461,7 +462,7 @@ struct GitLabInputScheme : GitArchiveInputScheme return std::make_pair(token.substr(0, fldsplit), token.substr(fldsplit + 1)); } - RefInfo getRevFromRef(nix::ref store, const Input & input) const override + RefInfo getRevFromRef(const Settings & settings, nix::ref store, const Input & input) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // See rate limiting note below @@ -472,9 +473,9 @@ struct GitLabInputScheme : GitArchiveInputScheme getStrAttr(input.attrs, "repo"), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); - auto downloadResult = downloadFile(store, *input.settings, url, "source", headers); + auto downloadResult = downloadFile(store, settings, url, "source", headers); auto json = nlohmann::json::parse(store->getFSAccessor(downloadResult.storePath)->readFile(CanonPath::root)); if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) { @@ -487,7 +488,7 @@ struct GitLabInputScheme : GitArchiveInputScheme } } - DownloadUrl getDownloadUrl(const Input & input) const override + DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override { // This endpoint has a rate limit threshold that may be // server-specific and vary based whether the user is @@ -502,19 +503,20 @@ struct GitLabInputScheme : GitArchiveInputScheme getStrAttr(input.attrs, "repo"), input.getRev()->gitRev()); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); return DownloadUrl{parseURL(url), headers}; } - void clone(ref store, const Input & input, const std::filesystem::path & destDir) const override + void clone(const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) + const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // FIXME: get username somewhere Input::fromURL( - *input.settings, + settings, fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) - .clone(store, destDir); + .clone(settings, store, destDir); } }; @@ -535,7 +537,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme // Once it is implemented, however, should work as expected. } - RefInfo getRevFromRef(nix::ref store, const Input & input) const override + RefInfo getRevFromRef(const Settings & settings, nix::ref store, const Input & input) const override { // TODO: In the future, when the sourcehut graphql API is implemented for mercurial // and with anonymous access, this method should use it instead. @@ -546,12 +548,12 @@ struct SourceHutInputScheme : GitArchiveInputScheme auto base_url = fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); std::string refUri; if (ref == "HEAD") { - auto file = store->toRealPath( - downloadFile(store, *input.settings, fmt("%s/HEAD", base_url), "source", headers).storePath); + auto file = + store->toRealPath(downloadFile(store, settings, fmt("%s/HEAD", base_url), "source", headers).storePath); std::ifstream is(file); std::string line; getline(is, line); @@ -567,7 +569,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme std::regex refRegex(refUri); auto file = store->toRealPath( - downloadFile(store, *input.settings, fmt("%s/info/refs", base_url), "source", headers).storePath); + downloadFile(store, settings, fmt("%s/info/refs", base_url), "source", headers).storePath); std::ifstream is(file); std::string line; @@ -584,7 +586,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme return RefInfo{.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)}; } - DownloadUrl getDownloadUrl(const Input & input) const override + DownloadUrl getDownloadUrl(const Settings & settings, const Input & input) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); auto url = @@ -594,18 +596,19 @@ struct SourceHutInputScheme : GitArchiveInputScheme getStrAttr(input.attrs, "repo"), input.getRev()->gitRev()); - Headers headers = makeHeadersWithAuthTokens(*input.settings, host, input); + Headers headers = makeHeadersWithAuthTokens(settings, host, input); return DownloadUrl{parseURL(url), headers}; } - void clone(ref store, const Input & input, const std::filesystem::path & destDir) const override + void clone(const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) + const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); Input::fromURL( - *input.settings, + settings, fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) - .clone(store, destDir); + .clone(settings, store, destDir); } }; diff --git a/src/libfetchers/include/nix/fetchers/fetchers.hh b/src/libfetchers/include/nix/fetchers/fetchers.hh index aaa2d88c64c..16cddb29e7e 100644 --- a/src/libfetchers/include/nix/fetchers/fetchers.hh +++ b/src/libfetchers/include/nix/fetchers/fetchers.hh @@ -36,13 +36,6 @@ struct Input { friend struct InputScheme; - const Settings * settings; - - Input(const Settings & settings) - : settings{&settings} - { - } - std::shared_ptr scheme; // note: can be null Attrs attrs; @@ -87,7 +80,7 @@ public: * attributes like a Git revision or NAR hash that uniquely * identify its contents. */ - bool isLocked() const; + bool isLocked(const Settings & settings) const; /** * Only for relative path flakes, i.e. 'path:./foo', returns the @@ -120,7 +113,7 @@ public: * Fetch the entire input into the Nix store, returning the * location in the Nix store and the locked input. */ - std::tuple, Input> fetchToStore(ref store) const; + std::tuple, Input> fetchToStore(const Settings & settings, ref store) const; /** * Check the locking attributes in `result` against @@ -140,17 +133,17 @@ public: * input without copying it to the store. Also return a possibly * unlocked input. */ - std::pair, Input> getAccessor(ref store) const; + std::pair, Input> getAccessor(const Settings & settings, ref store) const; private: - std::pair, Input> getAccessorUnchecked(ref store) const; + std::pair, Input> getAccessorUnchecked(const Settings & settings, ref store) const; public: Input applyOverrides(std::optional ref, std::optional rev) const; - void clone(ref store, const std::filesystem::path & destDir) const; + void clone(const Settings & settings, ref store, const std::filesystem::path & destDir) const; std::optional getSourcePath() const; @@ -223,7 +216,8 @@ struct InputScheme virtual Input applyOverrides(const Input & input, std::optional ref, std::optional rev) const; - virtual void clone(ref store, const Input & input, const std::filesystem::path & destDir) const; + virtual void clone( + const Settings & settings, ref store, const Input & input, const std::filesystem::path & destDir) const; virtual std::optional getSourcePath(const Input & input) const; @@ -233,7 +227,8 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; - virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; + virtual std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & input) const = 0; /** * Is this `InputScheme` part of an experimental feature? @@ -250,7 +245,7 @@ struct InputScheme return std::nullopt; } - virtual bool isLocked(const Input & input) const + virtual bool isLocked(const Settings & settings, const Input & input) const { return false; } diff --git a/src/libfetchers/include/nix/fetchers/input-cache.hh b/src/libfetchers/include/nix/fetchers/input-cache.hh index 40241207150..ad702dfdaa4 100644 --- a/src/libfetchers/include/nix/fetchers/input-cache.hh +++ b/src/libfetchers/include/nix/fetchers/input-cache.hh @@ -3,6 +3,7 @@ namespace nix::fetchers { enum class UseRegistries : int; +struct Settings; struct InputCache { @@ -14,7 +15,8 @@ struct InputCache Attrs extraAttrs; }; - CachedResult getAccessor(ref store, const Input & originalInput, UseRegistries useRegistries); + CachedResult + getAccessor(const Settings & settings, ref store, const Input & originalInput, UseRegistries useRegistries); struct CachedInput { diff --git a/src/libfetchers/include/nix/fetchers/registry.hh b/src/libfetchers/include/nix/fetchers/registry.hh index 90fc3d85368..bde5263611c 100644 --- a/src/libfetchers/include/nix/fetchers/registry.hh +++ b/src/libfetchers/include/nix/fetchers/registry.hh @@ -58,7 +58,7 @@ Path getUserRegistryPath(); Registries getRegistries(const Settings & settings, ref store); -void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs); +void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs); enum class UseRegistries : int { No, @@ -70,6 +70,7 @@ enum class UseRegistries : int { * Rewrite a flakeref using the registries. If `filter` is set, only * use the registries for which the filter function returns true. */ -std::pair lookupInRegistries(ref store, const Input & input, UseRegistries useRegistries); +std::pair +lookupInRegistries(const Settings & settings, ref store, const Input & input, UseRegistries useRegistries); } // namespace nix::fetchers diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index c9c9b0006a2..d36c6a183f1 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -44,7 +44,7 @@ struct IndirectInputScheme : InputScheme // FIXME: forbid query params? - Input input{settings}; + Input input{}; input.attrs.insert_or_assign("type", "indirect"); input.attrs.insert_or_assign("id", id); if (rev) @@ -76,7 +76,7 @@ struct IndirectInputScheme : InputScheme if (!std::regex_match(id, flakeRegex)) throw BadURL("'%s' is not a valid flake ID", id); - Input input{settings}; + Input input{}; input.attrs = attrs; return input; } @@ -106,7 +106,8 @@ struct IndirectInputScheme : InputScheme return input; } - std::pair, Input> getAccessor(ref store, const Input & input) const override + std::pair, Input> + getAccessor(const Settings & settings, 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-cache.cc b/src/libfetchers/input-cache.cc index c44f1a236b4..6da15bd3d62 100644 --- a/src/libfetchers/input-cache.cc +++ b/src/libfetchers/input-cache.cc @@ -5,23 +5,23 @@ namespace nix::fetchers { -InputCache::CachedResult -InputCache::getAccessor(ref store, const Input & originalInput, UseRegistries useRegistries) +InputCache::CachedResult InputCache::getAccessor( + const Settings & settings, ref store, const Input & originalInput, UseRegistries useRegistries) { auto fetched = lookup(originalInput); Input resolvedInput = originalInput; if (!fetched) { if (originalInput.isDirect()) { - auto [accessor, lockedInput] = originalInput.getAccessor(store); + auto [accessor, lockedInput] = originalInput.getAccessor(settings, store); fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor}); } else { if (useRegistries != UseRegistries::No) { - auto [res, extraAttrs] = lookupInRegistries(store, originalInput, useRegistries); + auto [res, extraAttrs] = lookupInRegistries(settings, store, originalInput, useRegistries); resolvedInput = std::move(res); fetched = lookup(resolvedInput); if (!fetched) { - auto [accessor, lockedInput] = resolvedInput.getAccessor(store); + auto [accessor, lockedInput] = resolvedInput.getAccessor(settings, store); fetched.emplace( CachedInput{.lockedInput = lockedInput, .accessor = accessor, .extraAttrs = extraAttrs}); } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 8fcc62483bd..c349e5dbf10 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -89,7 +89,7 @@ struct MercurialInputScheme : InputScheme throw BadURL("invalid Mercurial branch/tag name '%s'", *ref); } - Input input{settings}; + Input input{}; input.attrs = attrs; return input; } @@ -154,7 +154,7 @@ struct MercurialInputScheme : InputScheme return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()}; } - StorePath fetchToStore(ref store, Input & input) const + StorePath fetchToStore(const Settings & settings, ref store, Input & input) const { auto origRev = input.getRev(); @@ -176,10 +176,10 @@ struct MercurialInputScheme : InputScheme /* This is an unclean working tree. So copy all tracked files. */ - if (!input.settings->allowDirty) + if (!settings.allowDirty) throw Error("Mercurial tree '%s' is unclean", actualUrl); - if (input.settings->warnDirty) + if (settings.warnDirty) warn("Mercurial tree '%s' is unclean", actualUrl); input.attrs.insert_or_assign("ref", chomp(runHg({"branch", "-R", actualUrl}))); @@ -238,13 +238,13 @@ struct MercurialInputScheme : InputScheme Cache::Key refToRevKey{"hgRefToRev", {{"url", actualUrl}, {"ref", *input.getRef()}}}; if (!input.getRev()) { - if (auto res = input.settings->getCache()->lookupWithTTL(refToRevKey)) + if (auto res = settings.getCache()->lookupWithTTL(refToRevKey)) input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } /* If we have a rev, check if we have a cached store path. */ if (auto rev = input.getRev()) { - if (auto res = input.settings->getCache()->lookupStorePath(revInfoKey(*rev), *store)) + if (auto res = settings.getCache()->lookupStorePath(revInfoKey(*rev), *store)) return makeResult(res->value, res->storePath); } @@ -298,7 +298,7 @@ struct MercurialInputScheme : InputScheme /* Now that we have the rev, check the cache again for a cached store path. */ - if (auto res = input.settings->getCache()->lookupStorePath(revInfoKey(rev), *store)) + if (auto res = settings.getCache()->lookupStorePath(revInfoKey(rev), *store)) return makeResult(res->value, res->storePath); Path tmpDir = createTempDir(); @@ -315,18 +315,19 @@ struct MercurialInputScheme : InputScheme }); if (!origRev) - input.settings->getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}}); + settings.getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}}); - input.settings->getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath); + settings.getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath); return makeResult(infoAttrs, std::move(storePath)); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { Input input(_input); - auto storePath = fetchToStore(store, input); + auto storePath = fetchToStore(settings, store, input); // We just added it, it should be there. auto accessor = ref{store->getFSAccessor(storePath)}; @@ -336,7 +337,7 @@ struct MercurialInputScheme : InputScheme return {accessor, input}; } - bool isLocked(const Input & input) const override + bool isLocked(const Settings & settings, const Input & input) const override { return (bool) input.getRev(); } diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 8aefd091ff8..a1c3c1537e3 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -17,7 +17,7 @@ struct PathInputScheme : InputScheme if (url.authority && url.authority->host.size()) throw Error("path URL '%s' should not have an authority ('%s')", url, *url.authority); - Input input{settings}; + Input input{}; input.attrs.insert_or_assign("type", "path"); input.attrs.insert_or_assign("path", renderUrlPathEnsureLegal(url.path)); @@ -60,7 +60,7 @@ struct PathInputScheme : InputScheme { getStrAttr(attrs, "path"); - Input input{settings}; + Input input{}; input.attrs = attrs; return input; } @@ -101,7 +101,7 @@ struct PathInputScheme : InputScheme return path; } - bool isLocked(const Input & input) const override + bool isLocked(const Settings & settings, const Input & input) const override { return (bool) input.getNarHash(); } @@ -116,7 +116,8 @@ 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) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { Input input(_input); auto path = getStrAttr(input.attrs, "path"); @@ -145,7 +146,7 @@ struct PathInputScheme : InputScheme auto info = store->queryPathInfo(*storePath); accessor->fingerprint = fmt("path:%s", store->queryPathInfo(*storePath)->narHash.to_string(HashFormat::SRI, true)); - input.settings->getCache()->upsert( + settings.getCache()->upsert( makeSourcePathToHashCacheKey(*accessor->fingerprint, ContentAddressMethod::Raw::NixArchive, "/"), {{"hash", info->narHash.to_string(HashFormat::SRI, true)}}); diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index e570fc84b17..48dc96fcffa 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -124,9 +124,9 @@ std::shared_ptr getFlagRegistry(const Settings & settings) return flagRegistry; } -void overrideRegistry(const Input & from, const Input & to, const Attrs & extraAttrs) +void overrideRegistry(const Settings & settings, const Input & from, const Input & to, const Attrs & extraAttrs) { - getFlagRegistry(*from.settings)->add(from, to, extraAttrs); + getFlagRegistry(settings)->add(from, to, extraAttrs); } static std::shared_ptr getGlobalRegistry(const Settings & settings, ref store) @@ -160,7 +160,8 @@ Registries getRegistries(const Settings & settings, ref store) return registries; } -std::pair lookupInRegistries(ref store, const Input & _input, UseRegistries useRegistries) +std::pair +lookupInRegistries(const Settings & settings, ref store, const Input & _input, UseRegistries useRegistries) { Attrs extraAttrs; int n = 0; @@ -175,7 +176,7 @@ std::pair lookupInRegistries(ref store, const Input & _inpu if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); - for (auto & registry : getRegistries(*input.settings, store)) { + for (auto & registry : getRegistries(settings, store)) { if (useRegistries == UseRegistries::Limited && !(registry->type == fetchers::Registry::Flag || registry->type == fetchers::Registry::Global)) continue; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index e38853674ba..e94b066bd20 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -224,7 +224,7 @@ ref downloadTarball(ref store, const Settings & settings, auto input = Input::fromAttrs(settings, std::move(attrs)); - return input.getAccessor(store).first; + return input.getAccessor(settings, store).first; } // An input scheme corresponding to a curl-downloadable resource. @@ -252,7 +252,7 @@ struct CurlInputScheme : InputScheme if (!isValidURL(_url, requireTree)) return std::nullopt; - Input input{settings}; + Input input{}; auto url = _url; @@ -302,7 +302,7 @@ struct CurlInputScheme : InputScheme std::optional inputFromAttrs(const Settings & settings, const Attrs & attrs) const override { - Input input{settings}; + Input input{}; input.attrs = attrs; // input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); @@ -319,7 +319,7 @@ struct CurlInputScheme : InputScheme return url; } - bool isLocked(const Input & input) const override + bool isLocked(const Settings & settings, const Input & input) const override { return (bool) input.getNarHash(); } @@ -340,7 +340,8 @@ struct FileInputScheme : CurlInputScheme : (!requireTree && !hasTarballExtension(url))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { auto input(_input); @@ -348,7 +349,7 @@ struct FileInputScheme : CurlInputScheme the Nix store directly, since there is little deduplication benefit in using the Git cache for single big files like tarballs. */ - auto file = downloadFile(store, *input.settings, getStrAttr(input.attrs, "url"), input.getName()); + auto file = downloadFile(store, settings, getStrAttr(input.attrs, "url"), input.getName()); auto narHash = store->queryPathInfo(file.storePath)->narHash; input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); @@ -377,15 +378,15 @@ struct TarballInputScheme : CurlInputScheme : (requireTree || hasTarballExtension(url))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> + getAccessor(const Settings & settings, ref store, const Input & _input) const override { auto input(_input); - auto result = - downloadTarball_(*input.settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string(true) + "»"); + auto result = downloadTarball_(settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string(true) + "»"); if (result.immutableUrl) { - auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl); + auto immutableInput = Input::fromURL(settings, *result.immutableUrl); // FIXME: would be nice to support arbitrary flakerefs // here, e.g. git flakes. if (immutableInput.getType() != "tarball") @@ -398,9 +399,7 @@ struct TarballInputScheme : CurlInputScheme input.attrs.insert_or_assign( "narHash", - input.settings->getTarballCache() - ->treeHashToNarHash(*input.settings, result.treeHash) - .to_string(HashFormat::SRI, true)); + settings.getTarballCache()->treeHashToNarHash(settings, result.treeHash).to_string(HashFormat::SRI, true)); return {result.accessor, input}; } diff --git a/src/libflake/flake-primops.cc b/src/libflake/flake-primops.cc index 703463141f3..65de7ff18f1 100644 --- a/src/libflake/flake-primops.cc +++ b/src/libflake/flake-primops.cc @@ -12,7 +12,7 @@ PrimOp getFlake(const Settings & settings) std::string flakeRefS( state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); - if (state.settings.pureEval && !flakeRef.input.isLocked()) + if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings)) throw Error( "cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index c510872b37d..c5c00a43724 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -334,7 +334,8 @@ static Flake getFlake( bool requireLockable) { // Fetch a lazy tree first. - auto cachedInput = state.inputCache->getAccessor(state.store, originalRef.input, useRegistries); + auto cachedInput = + state.inputCache->getAccessor(state.fetchSettings, state.store, originalRef.input, useRegistries); auto subdir = fetchers::maybeGetStrAttr(cachedInput.extraAttrs, "dir").value_or(originalRef.subdir); auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), subdir); @@ -350,7 +351,8 @@ static Flake getFlake( debug("refetching input '%s' due to self attribute", newLockedRef); // FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'. newLockedRef.input.attrs.erase("narHash"); - auto cachedInput2 = state.inputCache->getAccessor(state.store, newLockedRef.input, fetchers::UseRegistries::No); + auto cachedInput2 = state.inputCache->getAccessor( + state.fetchSettings, state.store, newLockedRef.input, fetchers::UseRegistries::No); cachedInput.accessor = cachedInput2.accessor; lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir); } @@ -669,7 +671,8 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, this input. */ debug("creating new input '%s'", inputAttrPathS); - if (!lockFlags.allowUnlocked && !input.ref->input.isLocked() && !input.ref->input.isRelative()) + if (!lockFlags.allowUnlocked && !input.ref->input.isLocked(state.fetchSettings) + && !input.ref->input.isRelative()) throw Error("cannot update unlocked flake input '%s' in pure mode", inputAttrPathS); /* Note: in case of an --override-input, we use @@ -741,8 +744,8 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, if (auto resolvedPath = resolveRelativePath()) { return {*resolvedPath, *input.ref}; } else { - auto cachedInput = - state.inputCache->getAccessor(state.store, input.ref->input, useRegistriesTop); + auto cachedInput = state.inputCache->getAccessor( + state.fetchSettings, state.store, input.ref->input, useRegistriesTop); auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), input.ref->subdir); diff --git a/src/libflake/flakeref.cc b/src/libflake/flakeref.cc index a5a852ff5a2..e9986425b82 100644 --- a/src/libflake/flakeref.cc +++ b/src/libflake/flakeref.cc @@ -35,9 +35,10 @@ std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef) return str; } -FlakeRef FlakeRef::resolve(ref store, fetchers::UseRegistries useRegistries) const +FlakeRef FlakeRef::resolve( + const fetchers::Settings & fetchSettings, ref store, fetchers::UseRegistries useRegistries) const { - auto [input2, extraAttrs] = lookupInRegistries(store, input, useRegistries); + auto [input2, extraAttrs] = lookupInRegistries(fetchSettings, store, input, useRegistries); return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); } @@ -258,9 +259,10 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Settings & fetchSettings, const fet fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } -std::pair, FlakeRef> FlakeRef::lazyFetch(ref store) const +std::pair, FlakeRef> +FlakeRef::lazyFetch(const fetchers::Settings & fetchSettings, ref store) const { - auto [accessor, lockedInput] = input.getAccessor(store); + auto [accessor, lockedInput] = input.getAccessor(fetchSettings, store); return {accessor, FlakeRef(std::move(lockedInput), subdir)}; } diff --git a/src/libflake/include/nix/flake/flakeref.hh b/src/libflake/include/nix/flake/flakeref.hh index 629bc4677e6..d57d19e3104 100644 --- a/src/libflake/include/nix/flake/flakeref.hh +++ b/src/libflake/include/nix/flake/flakeref.hh @@ -64,11 +64,15 @@ struct FlakeRef fetchers::Attrs toAttrs() const; - FlakeRef resolve(ref store, fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const; + FlakeRef resolve( + const fetchers::Settings & fetchSettings, + ref store, + fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const; static FlakeRef fromAttrs(const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs); - std::pair, FlakeRef> lazyFetch(ref store) const; + std::pair, FlakeRef> + lazyFetch(const fetchers::Settings & fetchSettings, ref store) const; /** * Canonicalize a flakeref for the purpose of comparing "old" and diff --git a/src/libflake/lockfile.cc b/src/libflake/lockfile.cc index 089b614cbc4..1553a54430e 100644 --- a/src/libflake/lockfile.cc +++ b/src/libflake/lockfile.cc @@ -41,7 +41,7 @@ LockedNode::LockedNode(const fetchers::Settings & fetchSettings, const nlohmann: , parentInputAttrPath( json.find("parent") != json.end() ? (std::optional) json["parent"] : std::nullopt) { - if (!lockedRef.input.isLocked() && !lockedRef.input.isRelative()) { + if (!lockedRef.input.isLocked(fetchSettings) && !lockedRef.input.isRelative()) { if (lockedRef.input.getNarHash()) warn( "Lock file entry '%s' is unlocked (e.g. lacks a Git revision) but does have a NAR hash. " @@ -262,7 +262,7 @@ std::optional LockFile::isUnlocked(const fetchers::Settings & fetchSet latter case, we can verify the input but we may not be able to fetch it from anywhere. */ auto isConsideredLocked = [&](const fetchers::Input & input) { - return input.isLocked() || (fetchSettings.allowDirtyLocks && input.getNarHash()); + return input.isLocked(fetchSettings) || (fetchSettings.allowDirtyLocks && input.getNarHash()); }; for (auto & i : nodes) { diff --git a/src/nix/flake-prefetch-inputs.cc b/src/nix/flake-prefetch-inputs.cc index 9ee4b546e70..b89233877a1 100644 --- a/src/nix/flake-prefetch-inputs.cc +++ b/src/nix/flake-prefetch-inputs.cc @@ -46,7 +46,7 @@ struct CmdFlakePrefetchInputs : FlakeCommand if (auto lockedNode = dynamic_cast(&node)) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("fetching '%s'", lockedNode->lockedRef)); - auto accessor = lockedNode->lockedRef.input.getAccessor(store).first; + auto accessor = lockedNode->lockedRef.input.getAccessor(fetchSettings, store).first; if (!evalSettings.lazyTrees) fetchToStore( fetchSettings, *store, accessor, FetchMode::Copy, lockedNode->lockedRef.input.getName()); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 473a2e9f356..f810d83c135 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -255,7 +255,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON printJSON(j); } else { logger->cout(ANSI_BOLD "Resolved URL:" ANSI_NORMAL " %s", flake.resolvedRef.to_string()); - if (flake.lockedRef.input.isLocked()) + if (flake.lockedRef.input.isLocked(fetchSettings)) logger->cout(ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s", flake.lockedRef.to_string()); if (flake.description) logger->cout(ANSI_BOLD "Description:" ANSI_NORMAL " %s", *flake.description); @@ -1050,7 +1050,7 @@ struct CmdFlakeClone : FlakeCommand if (destDir.empty()) throw Error("missing flag '--dest'"); - getFlakeRef().resolve(store).input.clone(store, destDir); + getFlakeRef().resolve(fetchSettings, store).input.clone(fetchSettings, store, destDir); } }; @@ -1089,7 +1089,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun, MixNoCheckSigs StorePathSet sources; auto storePath = dryRun ? flake.flake.lockedRef.input.computeStorePath(*store) - : std::get(flake.flake.lockedRef.input.fetchToStore(store)); + : std::get(flake.flake.lockedRef.input.fetchToStore(fetchSettings, store)); sources.insert(storePath); @@ -1101,8 +1101,10 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun, MixNoCheckSigs if (auto inputNode = std::get_if<0>(&input)) { std::optional storePath; if (!(*inputNode)->lockedRef.input.isRelative()) { - storePath = dryRun ? (*inputNode)->lockedRef.input.computeStorePath(*store) - : std::get((*inputNode)->lockedRef.input.fetchToStore(store)); + storePath = + dryRun + ? (*inputNode)->lockedRef.input.computeStorePath(*store) + : std::get((*inputNode)->lockedRef.input.fetchToStore(fetchSettings, store)); sources.insert(*storePath); } if (json) { @@ -1440,8 +1442,8 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON void run(ref store) override { auto originalRef = getFlakeRef(); - auto resolvedRef = originalRef.resolve(store); - auto [accessor, lockedRef] = resolvedRef.lazyFetch(store); + auto resolvedRef = originalRef.resolve(fetchSettings, store); + auto [accessor, lockedRef] = resolvedRef.lazyFetch(getEvalState()->fetchSettings, store); auto storePath = fetchToStore(getEvalState()->fetchSettings, *store, accessor, FetchMode::Copy, lockedRef.input.getName()); auto hash = store->queryPathInfo(storePath)->narHash; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 3509ee11244..702633d96b0 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -711,7 +711,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf element.identifier()); continue; } - if (element.source->originalRef.input.isLocked()) { + if (element.source->originalRef.input.isLocked(getEvalState()->fetchSettings)) { warn( "Found package '%s', but it was added from a locked flake reference so it can't be upgraded!", element.identifier()); @@ -740,7 +740,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf assert(infop); auto & info = *infop; - if (info.flake.lockedRef.input.isLocked() && element.source->lockedRef == info.flake.lockedRef) + if (info.flake.lockedRef.input.isLocked(getEvalState()->fetchSettings) + && element.source->lockedRef == info.flake.lockedRef) continue; printInfo( diff --git a/src/nix/registry.cc b/src/nix/registry.cc index d9fcf09fc83..7c6f808967f 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -190,8 +190,9 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand auto ref = parseFlakeRef(fetchSettings, url); auto lockedRef = parseFlakeRef(fetchSettings, locked); registry->remove(ref.input); - auto resolved = lockedRef.resolve(store).input.getAccessor(store).second; - if (!resolved.isLocked()) + auto resolvedInput = lockedRef.resolve(fetchSettings, store).input; + auto resolved = resolvedInput.getAccessor(fetchSettings, store).second; + if (!resolved.isLocked(fetchSettings)) warn("flake '%s' is not locked", resolved.to_string()); fetchers::Attrs extraAttrs; if (ref.subdir != "") From 22bf19812f6c4ea29e782ada4db899c8f4d67230 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Nov 2025 20:31:53 +0100 Subject: [PATCH 2/2] repl: Fix incorrect error message --- src/libcmd/repl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ab5fc562f2c..a88365c8d50 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -740,7 +740,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS) auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true); if (evalSettings.pureEval && !flakeRef.input.isLocked(fetchSettings)) - throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); + throw Error("cannot use ':load-flake' on unlocked flake reference '%s' (use --impure to override)", flakeRefS); Value v;