1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-13 04:21:04 +01:00

Merge remote-tracking branch 'origin/master' into relative-flakes

This commit is contained in:
Eelco Dolstra 2024-11-22 14:44:32 +01:00
commit 0b00bf7c09
869 changed files with 5358 additions and 7769 deletions

View file

@ -3,6 +3,7 @@
#include "source-path.hh"
#include "fetch-to-store.hh"
#include "json-utils.hh"
#include "store-path-accessor.hh"
#include <nlohmann/json.hpp>
@ -100,7 +101,7 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
auto allowedAttrs = inputScheme->allowedAttrs();
for (auto & [name, _] : attrs)
if (name != "type" && allowedAttrs.count(name) == 0)
if (name != "type" && name != "__final" && allowedAttrs.count(name) == 0)
throw Error("input attribute '%s' not supported by scheme '%s'", name, schemeName);
auto res = inputScheme->inputFromAttrs(settings, attrs);
@ -145,6 +146,11 @@ bool Input::isLocked() const
return scheme && scheme->isLocked(*this);
}
bool Input::isFinal() const
{
return maybeGetBoolAttr(attrs, "__final").value_or(false);
}
std::optional<std::string> Input::isRelative() const
{
assert(scheme);
@ -178,16 +184,24 @@ std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try {
auto [accessor, final] = getAccessorUnchecked(store);
auto [accessor, result] = getAccessorUnchecked(store);
auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, final.getName());
auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, result.getName());
auto narHash = store->queryPathInfo(storePath)->narHash;
final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
scheme->checkLocks(*this, final);
// FIXME: we would like to mark inputs as final in
// getAccessorUnchecked(), but then we can't add
// narHash. Or maybe narHash should be excluded from the
// concept of "final" inputs?
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
return {storePath, final};
assert(result.isFinal());
checkLocks(*this, result);
return {storePath, result};
} catch (Error & e) {
e.addTrace({}, "while fetching the input '%s'", to_string());
throw;
@ -197,13 +211,40 @@ std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
return {std::move(storePath), input};
}
void InputScheme::checkLocks(const Input & specified, const Input & final) const
void Input::checkLocks(Input specified, Input & result)
{
/* If the original input is final, then we just return the
original attributes, dropping any new fields returned by the
fetcher. However, any fields that are in both the specified and
result input must be identical. */
if (specified.isFinal()) {
/* Backwards compatibility hack: we had some lock files in the
past that 'narHash' fields with incorrect base-64
formatting (lacking the trailing '=', e.g. 'sha256-ri...Mw'
instead of ''sha256-ri...Mw='). So fix that. */
if (auto prevNarHash = specified.getNarHash())
specified.attrs.insert_or_assign("narHash", prevNarHash->to_string(HashFormat::SRI, true));
for (auto & field : specified.attrs) {
auto field2 = result.attrs.find(field.first);
if (field2 != result.attrs.end() && field.second != field2->second)
throw Error("mismatch in field '%s' of input '%s', got '%s'",
field.first,
attrsToJSON(specified.attrs),
attrsToJSON(result.attrs));
}
result.attrs = specified.attrs;
return;
}
if (auto prevNarHash = specified.getNarHash()) {
if (final.getNarHash() != prevNarHash) {
if (final.getNarHash())
if (result.getNarHash() != prevNarHash) {
if (result.getNarHash())
throw Error((unsigned int) 102, "NAR hash mismatch in input '%s', expected '%s' but got '%s'",
specified.to_string(), prevNarHash->to_string(HashFormat::SRI, true), final.getNarHash()->to_string(HashFormat::SRI, true));
specified.to_string(), prevNarHash->to_string(HashFormat::SRI, true), result.getNarHash()->to_string(HashFormat::SRI, true));
else
throw Error((unsigned int) 102, "NAR hash mismatch in input '%s', expected '%s' but got none",
specified.to_string(), prevNarHash->to_string(HashFormat::SRI, true));
@ -211,32 +252,32 @@ void InputScheme::checkLocks(const Input & specified, const Input & final) const
}
if (auto prevLastModified = specified.getLastModified()) {
if (final.getLastModified() != prevLastModified)
throw Error("'lastModified' attribute mismatch in input '%s', expected %d",
final.to_string(), *prevLastModified);
if (result.getLastModified() != prevLastModified)
throw Error("'lastModified' attribute mismatch in input '%s', expected %d, got %d",
result.to_string(), *prevLastModified, result.getLastModified().value_or(-1));
}
if (auto prevRev = specified.getRev()) {
if (final.getRev() != prevRev)
if (result.getRev() != prevRev)
throw Error("'rev' attribute mismatch in input '%s', expected %s",
final.to_string(), prevRev->gitRev());
result.to_string(), prevRev->gitRev());
}
if (auto prevRevCount = specified.getRevCount()) {
if (final.getRevCount() != prevRevCount)
if (result.getRevCount() != prevRevCount)
throw Error("'revCount' attribute mismatch in input '%s', expected %d",
final.to_string(), *prevRevCount);
result.to_string(), *prevRevCount);
}
}
std::pair<ref<SourceAccessor>, Input> Input::getAccessor(ref<Store> store) const
{
try {
auto [accessor, final] = getAccessorUnchecked(store);
auto [accessor, result] = getAccessorUnchecked(store);
scheme->checkLocks(*this, final);
checkLocks(*this, result);
return {accessor, std::move(final)};
return {accessor, std::move(result)};
} catch (Error & e) {
e.addTrace({}, "while fetching the input '%s'", to_string());
throw;
@ -250,12 +291,42 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
auto [accessor, final] = scheme->getAccessor(store, *this);
/* The tree may already be in the Nix store, or it could be
substituted (which is often faster than fetching from the
original source). So check that. We only do this for final
inputs, otherwise there is a risk that we don't return the
same attributes (like `lastModified`) that the "real" fetcher
would return.
FIXME: add a setting to disable this.
FIXME: substituting may be slower than fetching normally,
e.g. for fetchers like Git that are incremental!
*/
if (isFinal() && getNarHash()) {
try {
auto storePath = computeStorePath(*store);
store->ensurePath(storePath);
debug("using substituted/cached input '%s' in '%s'",
to_string(), store->printStorePath(storePath));
auto accessor = makeStorePathAccessor(store, storePath);
accessor->fingerprint = scheme->getFingerprint(store, *this);
return {accessor, *this};
} catch (Error & e) {
debug("substitution of input '%s' failed: %s", to_string(), e.what());
}
}
auto [accessor, result] = scheme->getAccessor(store, *this);
assert(!accessor->fingerprint);
accessor->fingerprint = scheme->getFingerprint(store, final);
accessor->fingerprint = scheme->getFingerprint(store, result);
return {accessor, std::move(final)};
return {accessor, std::move(result)};
}
Input Input::applyOverrides(
@ -410,7 +481,10 @@ namespace nlohmann {
using namespace nix;
fetchers::PublicKey adl_serializer<fetchers::PublicKey>::from_json(const json & json) {
#ifndef DOXYGEN_SKIP
fetchers::PublicKey adl_serializer<fetchers::PublicKey>::from_json(const json & json)
{
fetchers::PublicKey res = { };
if (auto type = optionalValueAt(json, "type"))
res.type = getString(*type);
@ -420,9 +494,12 @@ fetchers::PublicKey adl_serializer<fetchers::PublicKey>::from_json(const json &
return res;
}
void adl_serializer<fetchers::PublicKey>::to_json(json & json, fetchers::PublicKey p) {
void adl_serializer<fetchers::PublicKey>::to_json(json & json, fetchers::PublicKey p)
{
json["type"] = p.type;
json["key"] = p.key;
}
#endif
}