forked from lix-project/lix
950b46821f
The attributes previously stored in TreeInfo (narHash, revCount, lastModified) are now stored in Input. This makes it less arbitrary what attributes are stored where. As a result, the lock file format has changed. An entry like "info": { "lastModified": 1585405475, "narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE=" }, "locked": { "owner": "NixOS", "repo": "nixpkgs", "rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be", "type": "github" }, is now stored as "locked": { "owner": "NixOS", "repo": "nixpkgs", "rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be", "type": "github", "lastModified": 1585405475, "narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE=" }, The 'Input' class is now a dumb set of attributes. All the fetcher implementations subclass InputScheme, not Input. This simplifies the API. Also, fix substitution of flake inputs. This was broken since lazy flake fetching started using fetchTree internally.
239 lines
5.5 KiB
C++
239 lines
5.5 KiB
C++
#include "fetchers.hh"
|
|
#include "store-api.hh"
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
namespace nix::fetchers {
|
|
|
|
std::unique_ptr<std::vector<std::shared_ptr<InputScheme>>> inputSchemes = nullptr;
|
|
|
|
void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
|
|
{
|
|
if (!inputSchemes) inputSchemes = std::make_unique<std::vector<std::shared_ptr<InputScheme>>>();
|
|
inputSchemes->push_back(std::move(inputScheme));
|
|
}
|
|
|
|
Input Input::fromURL(const std::string & url)
|
|
{
|
|
return fromURL(parseURL(url));
|
|
}
|
|
|
|
static void fixupInput(Input & input)
|
|
{
|
|
// Check common attributes.
|
|
input.getType();
|
|
input.getRef();
|
|
if (input.getRev())
|
|
input.immutable = true;
|
|
input.getRevCount();
|
|
input.getLastModified();
|
|
if (input.getNarHash())
|
|
input.immutable = true;
|
|
}
|
|
|
|
Input Input::fromURL(const ParsedURL & url)
|
|
{
|
|
for (auto & inputScheme : *inputSchemes) {
|
|
auto res = inputScheme->inputFromURL(url);
|
|
if (res) {
|
|
res->scheme = inputScheme;
|
|
fixupInput(*res);
|
|
return std::move(*res);
|
|
}
|
|
}
|
|
|
|
throw Error("input '%s' is unsupported", url.url);
|
|
}
|
|
|
|
Input Input::fromAttrs(Attrs && attrs)
|
|
{
|
|
for (auto & inputScheme : *inputSchemes) {
|
|
auto res = inputScheme->inputFromAttrs(attrs);
|
|
if (res) {
|
|
res->scheme = inputScheme;
|
|
fixupInput(*res);
|
|
return std::move(*res);
|
|
}
|
|
}
|
|
|
|
Input input;
|
|
input.attrs = attrs;
|
|
fixupInput(input);
|
|
return input;
|
|
}
|
|
|
|
ParsedURL Input::toURL() const
|
|
{
|
|
if (!scheme)
|
|
throw Error("cannot show unsupported input '%s'", attrsToJson(attrs));
|
|
return scheme->toURL(*this);
|
|
}
|
|
|
|
std::string Input::to_string() const
|
|
{
|
|
return toURL().to_string();
|
|
}
|
|
|
|
Attrs Input::toAttrs() const
|
|
{
|
|
return attrs;
|
|
}
|
|
|
|
bool Input::hasAllInfo() const
|
|
{
|
|
return getNarHash() && scheme && scheme->hasAllInfo(*this);
|
|
}
|
|
|
|
bool Input::operator ==(const Input & other) const
|
|
{
|
|
return attrs == other.attrs;
|
|
}
|
|
|
|
bool Input::contains(const Input & other) const
|
|
{
|
|
auto other2(other);
|
|
other2.attrs.erase("ref");
|
|
other2.attrs.erase("rev");
|
|
if (*this == other2) return true;
|
|
return false;
|
|
}
|
|
|
|
std::pair<Tree, Input> Input::fetch(ref<Store> store) const
|
|
{
|
|
if (!scheme)
|
|
throw Error("cannot fetch unsupported input '%s'", attrsToJson(toAttrs()));
|
|
|
|
auto [tree, input] = scheme->fetch(store, *this);
|
|
|
|
if (tree.actualPath == "")
|
|
tree.actualPath = store->toRealPath(tree.storePath);
|
|
|
|
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
|
|
input.attrs.insert_or_assign("narHash", narHash.to_string(SRI));
|
|
|
|
if (auto narHash2 = getNarHash()) {
|
|
if (narHash != *narHash2)
|
|
throw Error("NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
|
|
to_string(), tree.actualPath, narHash2->to_string(SRI), narHash.to_string(SRI));
|
|
}
|
|
|
|
// FIXME: check lastModified, revCount
|
|
|
|
input.immutable = true;
|
|
|
|
assert(input.hasAllInfo());
|
|
|
|
return {std::move(tree), input};
|
|
}
|
|
|
|
Input Input::applyOverrides(
|
|
std::optional<std::string> ref,
|
|
std::optional<Hash> rev) const
|
|
{
|
|
if (!scheme) return *this;
|
|
return scheme->applyOverrides(*this, ref, rev);
|
|
}
|
|
|
|
void Input::clone(const Path & destDir) const
|
|
{
|
|
assert(scheme);
|
|
scheme->clone(*this, destDir);
|
|
}
|
|
|
|
std::optional<Path> Input::getSourcePath() const
|
|
{
|
|
assert(scheme);
|
|
return scheme->getSourcePath(*this);
|
|
}
|
|
|
|
void Input::markChangedFile(
|
|
std::string_view file,
|
|
std::optional<std::string> commitMsg) const
|
|
{
|
|
assert(scheme);
|
|
return scheme->markChangedFile(*this, file, commitMsg);
|
|
}
|
|
|
|
StorePath Input::computeStorePath(Store & store) const
|
|
{
|
|
auto narHash = getNarHash();
|
|
if (!narHash)
|
|
throw Error("cannot compute store path for mutable input '%s'", to_string());
|
|
return store.makeFixedOutputPath(true, *narHash, "source");
|
|
}
|
|
|
|
std::string Input::getType() const
|
|
{
|
|
return getStrAttr(attrs, "type");
|
|
}
|
|
|
|
std::optional<Hash> Input::getNarHash() const
|
|
{
|
|
if (auto s = maybeGetStrAttr(attrs, "narHash"))
|
|
// FIXME: require SRI hash.
|
|
return Hash(*s, htSHA256);
|
|
return {};
|
|
}
|
|
|
|
std::optional<std::string> Input::getRef() const
|
|
{
|
|
if (auto s = maybeGetStrAttr(attrs, "ref"))
|
|
return *s;
|
|
return {};
|
|
}
|
|
|
|
std::optional<Hash> Input::getRev() const
|
|
{
|
|
if (auto s = maybeGetStrAttr(attrs, "rev"))
|
|
return Hash(*s, htSHA1);
|
|
return {};
|
|
}
|
|
|
|
std::optional<uint64_t> Input::getRevCount() const
|
|
{
|
|
if (auto n = maybeGetIntAttr(attrs, "revCount"))
|
|
return *n;
|
|
return {};
|
|
}
|
|
|
|
std::optional<time_t> Input::getLastModified() const
|
|
{
|
|
if (auto n = maybeGetIntAttr(attrs, "lastModified"))
|
|
return *n;
|
|
return {};
|
|
}
|
|
|
|
ParsedURL InputScheme::toURL(const Input & input)
|
|
{
|
|
throw Error("don't know how to convert input '%s' to a URL", attrsToJson(input.attrs));
|
|
}
|
|
|
|
Input InputScheme::applyOverrides(
|
|
const Input & input,
|
|
std::optional<std::string> ref,
|
|
std::optional<Hash> rev)
|
|
{
|
|
if (ref)
|
|
throw Error("don't know how to set branch/tag name of input '%s' to '%s'", input.to_string(), *ref);
|
|
if (rev)
|
|
throw Error("don't know how to set revision of input '%s' to '%s'", input.to_string(), rev->gitRev());
|
|
return input;
|
|
}
|
|
|
|
std::optional<Path> InputScheme::getSourcePath(const Input & input)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
void InputScheme::markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
void InputScheme::clone(const Input & input, const Path & destDir)
|
|
{
|
|
throw Error("do not know how to clone input '%s'", input.to_string());
|
|
}
|
|
|
|
}
|