1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 14:51:16 +02:00

lockFlake(): Allow registry lookups for the top-level flake

Fixes #13050.
This commit is contained in:
Eelco Dolstra 2025-04-24 18:54:16 +02:00
parent dda265f09a
commit 68de26d38a
11 changed files with 53 additions and 26 deletions

View file

@ -40,7 +40,7 @@ void completeFlakeInputAttrPath(
std::string_view prefix) std::string_view prefix)
{ {
for (auto & flakeRef : flakeRefs) { for (auto & flakeRef : flakeRefs) {
auto flake = flake::getFlake(*evalState, flakeRef, true); auto flake = flake::getFlake(*evalState, flakeRef, fetchers::UseRegistries::All);
for (auto & input : flake.inputs) for (auto & input : flake.inputs)
if (hasPrefix(input.first, prefix)) if (hasPrefix(input.first, prefix))
completions.add(input.first); completions.add(input.first);

View file

@ -181,7 +181,7 @@ static void fetchTree(
} }
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
input = lookupInRegistries(state.store, input).first; input = lookupInRegistries(state.store, input, fetchers::UseRegistries::Limited).first;
if (state.settings.pureEval && !input.isLocked()) { if (state.settings.pureEval && !input.isLocked()) {
if (input.getNarHash()) if (input.getNarHash())

View file

@ -2,6 +2,8 @@
namespace nix::fetchers { namespace nix::fetchers {
enum class UseRegistries : int;
struct InputCache struct InputCache
{ {
struct CachedResult struct CachedResult
@ -11,7 +13,7 @@ struct InputCache
Input lockedInput; Input lockedInput;
}; };
CachedResult getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries); CachedResult getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries);
struct CachedInput struct CachedInput
{ {

View file

@ -65,7 +65,11 @@ void overrideRegistry(
const Input & to, const Input & to,
const Attrs & extraAttrs); const Attrs & extraAttrs);
using RegistryFilter = std::function<bool(Registry::RegistryType)>; enum class UseRegistries : int {
No,
All,
Limited, // global and flag registry only
};
/** /**
* Rewrite a flakeref using the registries. If `filter` is set, only * Rewrite a flakeref using the registries. If `filter` is set, only
@ -74,6 +78,6 @@ using RegistryFilter = std::function<bool(Registry::RegistryType)>;
std::pair<Input, Attrs> lookupInRegistries( std::pair<Input, Attrs> lookupInRegistries(
ref<Store> store, ref<Store> store,
const Input & input, const Input & input,
const RegistryFilter & filter = {}); UseRegistries useRegistries);
} }

View file

@ -5,7 +5,8 @@
namespace nix::fetchers { namespace nix::fetchers {
InputCache::CachedResult InputCache::getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries) InputCache::CachedResult
InputCache::getAccessor(ref<Store> store, const Input & originalInput, UseRegistries useRegistries)
{ {
auto fetched = lookup(originalInput); auto fetched = lookup(originalInput);
Input resolvedInput = originalInput; Input resolvedInput = originalInput;
@ -15,13 +16,8 @@ InputCache::CachedResult InputCache::getAccessor(ref<Store> store, const Input &
auto [accessor, lockedInput] = originalInput.getAccessor(store); auto [accessor, lockedInput] = originalInput.getAccessor(store);
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor}); fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
} else { } else {
if (useRegistries) { if (useRegistries != UseRegistries::No) {
auto [res, extraAttrs] = auto [res, extraAttrs] = lookupInRegistries(store, originalInput, useRegistries);
lookupInRegistries(store, originalInput, [](fetchers::Registry::RegistryType type) {
/* Only use the global registry and CLI flags
to resolve indirect flakerefs. */
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
});
resolvedInput = std::move(res); resolvedInput = std::move(res);
fetched = lookup(resolvedInput); fetched = lookup(resolvedInput);
if (!fetched) { if (!fetched) {

View file

@ -14,6 +14,8 @@ std::shared_ptr<Registry> Registry::read(
const Settings & settings, const Settings & settings,
const Path & path, RegistryType type) const Path & path, RegistryType type)
{ {
debug("reading registry '%s'", path);
auto registry = std::make_shared<Registry>(settings, type); auto registry = std::make_shared<Registry>(settings, type);
if (!pathExists(path)) if (!pathExists(path))
@ -179,29 +181,36 @@ Registries getRegistries(const Settings & settings, ref<Store> store)
std::pair<Input, Attrs> lookupInRegistries( std::pair<Input, Attrs> lookupInRegistries(
ref<Store> store, ref<Store> store,
const Input & _input, const Input & _input,
const RegistryFilter & filter) UseRegistries useRegistries)
{ {
Attrs extraAttrs; Attrs extraAttrs;
int n = 0; int n = 0;
Input input(_input); Input input(_input);
if (useRegistries == UseRegistries::No)
return {input, extraAttrs};
restart: restart:
n++; n++;
if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); 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(*input.settings, store)) {
if (filter && !filter(registry->type)) continue; if (useRegistries == UseRegistries::Limited
&& !(registry->type == fetchers::Registry::Flag || registry->type == fetchers::Registry::Global))
continue;
// FIXME: O(n) // FIXME: O(n)
for (auto & entry : registry->entries) { for (auto & entry : registry->entries) {
if (entry.exact) { if (entry.exact) {
if (entry.from == input) { if (entry.from == input) {
debug("resolved flakeref '%s' against registry %d exactly", input.to_string(), registry->type);
input = entry.to; input = entry.to;
extraAttrs = entry.extraAttrs; extraAttrs = entry.extraAttrs;
goto restart; goto restart;
} }
} else { } else {
if (entry.from.contains(input)) { if (entry.from.contains(input)) {
debug("resolved flakeref '%s' against registry %d", input.to_string(), registry->type);
input = entry.to.applyOverrides( input = entry.to.applyOverrides(
!entry.from.getRef() && input.getRef() ? input.getRef() : std::optional<std::string>(), !entry.from.getRef() && input.getRef() ? input.getRef() : std::optional<std::string>(),
!entry.from.getRev() && input.getRev() ? input.getRev() : std::optional<Hash>()); !entry.from.getRev() && input.getRev() ? input.getRev() : std::optional<Hash>());

View file

@ -337,7 +337,7 @@ static FlakeRef applySelfAttrs(
static Flake getFlake( static Flake getFlake(
EvalState & state, EvalState & state,
const FlakeRef & originalRef, const FlakeRef & originalRef,
bool useRegistries, fetchers::UseRegistries useRegistries,
const InputAttrPath & lockRootAttrPath) const InputAttrPath & lockRootAttrPath)
{ {
// Fetch a lazy tree first. // Fetch a lazy tree first.
@ -368,7 +368,7 @@ static Flake getFlake(
return readFlake(state, originalRef, resolvedRef, lockedRef, state.storePath(storePath), lockRootAttrPath); return readFlake(state, originalRef, resolvedRef, lockedRef, state.storePath(storePath), lockRootAttrPath);
} }
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries) Flake getFlake(EvalState & state, const FlakeRef & originalRef, fetchers::UseRegistries useRegistries)
{ {
return getFlake(state, originalRef, useRegistries, {}); return getFlake(state, originalRef, useRegistries, {});
} }
@ -393,8 +393,14 @@ LockedFlake lockFlake(
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
auto useRegistriesTop = useRegistries ? fetchers::UseRegistries::All : fetchers::UseRegistries::No;
auto useRegistriesInputs = useRegistries ? fetchers::UseRegistries::Limited : fetchers::UseRegistries::No;
auto flake = getFlake(state, topRef, useRegistries, {}); auto flake = getFlake(
state,
topRef,
useRegistriesTop,
{});
if (lockFlags.applyNixConfig) { if (lockFlags.applyNixConfig) {
flake.config.apply(settings); flake.config.apply(settings);
@ -569,7 +575,11 @@ LockedFlake lockFlake(
if (auto resolvedPath = resolveRelativePath()) { if (auto resolvedPath = resolveRelativePath()) {
return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath); return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath);
} else { } else {
return getFlake(state, ref, useRegistries, inputAttrPath); return getFlake(
state,
ref,
useRegistriesInputs,
inputAttrPath);
} }
}; };
@ -717,7 +727,10 @@ LockedFlake lockFlake(
if (auto resolvedPath = resolveRelativePath()) { if (auto resolvedPath = resolveRelativePath()) {
return {*resolvedPath, *input.ref}; return {*resolvedPath, *input.ref};
} else { } else {
auto cachedInput = state.inputCache->getAccessor(state.store, input.ref->input, useRegistries); auto cachedInput = state.inputCache->getAccessor(
state.store,
input.ref->input,
useRegistriesInputs);
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir); auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);
@ -834,7 +847,10 @@ LockedFlake lockFlake(
repo, so we should re-read it. FIXME: we could repo, so we should re-read it. FIXME: we could
also just clear the 'rev' field... */ also just clear the 'rev' field... */
auto prevLockedRef = flake.lockedRef; auto prevLockedRef = flake.lockedRef;
flake = getFlake(state, topRef, useRegistries); flake = getFlake(
state,
topRef,
useRegistriesTop);
if (lockFlags.commitLockFile && if (lockFlags.commitLockFile &&
flake.lockedRef.input.getRev() && flake.lockedRef.input.getRev() &&

View file

@ -37,9 +37,9 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef)
FlakeRef FlakeRef::resolve( FlakeRef FlakeRef::resolve(
ref<Store> store, ref<Store> store,
const fetchers::RegistryFilter & filter) const fetchers::UseRegistries useRegistries) const
{ {
auto [input2, extraAttrs] = lookupInRegistries(store, input, filter); auto [input2, extraAttrs] = lookupInRegistries(store, input, useRegistries);
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
} }

View file

@ -115,7 +115,7 @@ struct Flake
} }
}; };
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool useRegistries); Flake getFlake(EvalState & state, const FlakeRef & flakeRef, fetchers::UseRegistries useRegistries);
/** /**
* Fingerprint of a locked flake; used as a cache key. * Fingerprint of a locked flake; used as a cache key.

View file

@ -65,7 +65,7 @@ struct FlakeRef
FlakeRef resolve( FlakeRef resolve(
ref<Store> store, ref<Store> store,
const fetchers::RegistryFilter & filter = {}) const; fetchers::UseRegistries useRegistries = fetchers::UseRegistries::All) const;
static FlakeRef fromAttrs( static FlakeRef fromAttrs(
const fetchers::Settings & fetchSettings, const fetchers::Settings & fetchSettings,

View file

@ -223,7 +223,7 @@ mv "$registry.tmp" "$registry"
# Ensure that locking ignores the user registry. # Ensure that locking ignores the user registry.
mkdir -p "$TEST_HOME/.config/nix" mkdir -p "$TEST_HOME/.config/nix"
ln -sfn "$registry" "$TEST_HOME/.config/nix/registry.json" ln -sfn "$registry" "$TEST_HOME/.config/nix/registry.json"
nix flake metadata flake1 nix flake metadata --flake-registry '' flake1
expectStderr 1 nix flake update --flake-registry '' --flake "$flake3Dir" | grepQuiet "cannot find flake 'flake:flake1' in the flake registries" expectStderr 1 nix flake update --flake-registry '' --flake "$flake3Dir" | grepQuiet "cannot find flake 'flake:flake1' in the flake registries"
rm "$TEST_HOME/.config/nix/registry.json" rm "$TEST_HOME/.config/nix/registry.json"