2020-03-18 13:08:25 +00:00
|
|
|
#include "fetchers/registry.hh"
|
|
|
|
#include "fetchers/fetchers.hh"
|
2020-01-21 15:27:53 +00:00
|
|
|
#include "util.hh"
|
|
|
|
#include "globals.hh"
|
|
|
|
#include "download.hh"
|
2020-03-18 13:08:25 +00:00
|
|
|
#include "store-api.hh"
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
|
|
|
namespace nix::fetchers {
|
|
|
|
|
|
|
|
std::shared_ptr<Registry> Registry::read(
|
|
|
|
const Path & path, RegistryType type)
|
|
|
|
{
|
2020-01-22 19:00:58 +00:00
|
|
|
auto registry = std::make_shared<Registry>(type);
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
if (!pathExists(path))
|
2020-01-22 19:00:58 +00:00
|
|
|
return std::make_shared<Registry>(type);
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
auto json = nlohmann::json::parse(readFile(path));
|
|
|
|
|
|
|
|
auto version = json.value("version", 0);
|
|
|
|
|
2020-02-06 13:27:31 +00:00
|
|
|
// FIXME: remove soon
|
|
|
|
if (version == 1) {
|
|
|
|
auto flakes = json["flakes"];
|
|
|
|
for (auto i = flakes.begin(); i != flakes.end(); ++i) {
|
|
|
|
auto url = i->value("url", i->value("uri", ""));
|
|
|
|
if (url.empty())
|
|
|
|
throw Error("flake registry '%s' lacks a 'url' attribute for entry '%s'",
|
|
|
|
path, i.key());
|
|
|
|
registry->entries.push_back(
|
2020-02-20 21:14:44 +00:00
|
|
|
{inputFromURL(i.key()), inputFromURL(url), {}});
|
2020-02-06 13:27:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (version == 2) {
|
2020-02-20 21:14:44 +00:00
|
|
|
for (auto & i : json["flakes"]) {
|
|
|
|
auto toAttrs = jsonToAttrs(i["to"]);
|
2020-03-17 19:54:36 +00:00
|
|
|
Attrs extraAttrs;
|
2020-02-20 22:44:06 +00:00
|
|
|
auto j = toAttrs.find("dir");
|
2020-02-20 21:14:44 +00:00
|
|
|
if (j != toAttrs.end()) {
|
|
|
|
extraAttrs.insert(*j);
|
|
|
|
toAttrs.erase(j);
|
|
|
|
}
|
2020-02-06 13:27:31 +00:00
|
|
|
registry->entries.push_back(
|
|
|
|
{ inputFromAttrs(jsonToAttrs(i["from"]))
|
2020-02-20 21:14:44 +00:00
|
|
|
, inputFromAttrs(toAttrs)
|
|
|
|
, extraAttrs
|
2020-02-06 13:27:31 +00:00
|
|
|
});
|
2020-02-20 21:14:44 +00:00
|
|
|
}
|
2020-01-21 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 13:27:31 +00:00
|
|
|
else
|
|
|
|
throw Error("flake registry '%s' has unsupported version %d", path, version);
|
|
|
|
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
return registry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Registry::write(const Path & path)
|
|
|
|
{
|
2020-02-06 13:27:31 +00:00
|
|
|
nlohmann::json arr;
|
|
|
|
for (auto & elem : entries) {
|
|
|
|
nlohmann::json obj;
|
2020-02-20 21:14:44 +00:00
|
|
|
obj["from"] = attrsToJson(std::get<0>(elem)->toAttrs());
|
|
|
|
obj["to"] = attrsToJson(std::get<1>(elem)->toAttrs());
|
2020-02-20 22:47:02 +00:00
|
|
|
if (!std::get<2>(elem).empty())
|
|
|
|
obj["to"].update(attrsToJson(std::get<2>(elem)));
|
2020-02-06 13:27:31 +00:00
|
|
|
arr.emplace_back(std::move(obj));
|
|
|
|
}
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
nlohmann::json json;
|
2020-02-06 13:27:31 +00:00
|
|
|
json["version"] = 2;
|
|
|
|
json["flakes"] = std::move(arr);
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
createDirs(dirOf(path));
|
2020-02-06 13:27:31 +00:00
|
|
|
writeFile(path, json.dump(2));
|
2020-01-21 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Registry::add(
|
|
|
|
const std::shared_ptr<const Input> & from,
|
2020-02-20 21:14:44 +00:00
|
|
|
const std::shared_ptr<const Input> & to,
|
2020-03-17 19:54:36 +00:00
|
|
|
const Attrs & extraAttrs)
|
2020-01-21 15:27:53 +00:00
|
|
|
{
|
2020-02-20 21:14:44 +00:00
|
|
|
entries.emplace_back(from, to, extraAttrs);
|
2020-01-21 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Registry::remove(const std::shared_ptr<const Input> & input)
|
|
|
|
{
|
|
|
|
// FIXME: use C++20 std::erase.
|
|
|
|
for (auto i = entries.begin(); i != entries.end(); )
|
2020-02-20 21:14:44 +00:00
|
|
|
if (*std::get<0>(*i) == *input)
|
2020-01-21 15:27:53 +00:00
|
|
|
i = entries.erase(i);
|
|
|
|
else
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
Path getUserRegistryPath()
|
|
|
|
{
|
|
|
|
return getHome() + "/.config/nix/registry.json";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Registry> getUserRegistry()
|
|
|
|
{
|
|
|
|
return Registry::read(getUserRegistryPath(), Registry::User);
|
|
|
|
}
|
|
|
|
|
2020-01-22 19:00:58 +00:00
|
|
|
static std::shared_ptr<Registry> flagRegistry =
|
|
|
|
std::make_shared<Registry>(Registry::Flag);
|
|
|
|
|
|
|
|
std::shared_ptr<Registry> getFlagRegistry()
|
2020-01-21 15:27:53 +00:00
|
|
|
{
|
|
|
|
return flagRegistry;
|
|
|
|
}
|
2020-01-22 19:00:58 +00:00
|
|
|
|
|
|
|
void overrideRegistry(
|
|
|
|
const std::shared_ptr<const Input> & from,
|
2020-02-20 21:14:44 +00:00
|
|
|
const std::shared_ptr<const Input> & to,
|
2020-03-17 19:54:36 +00:00
|
|
|
const Attrs & extraAttrs)
|
2020-01-22 19:00:58 +00:00
|
|
|
{
|
2020-02-20 21:14:44 +00:00
|
|
|
flagRegistry->add(from, to, extraAttrs);
|
2020-01-22 19:00:58 +00:00
|
|
|
}
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
|
|
|
|
{
|
|
|
|
static auto reg = [&]() {
|
|
|
|
auto path = settings.flakeRegistry;
|
|
|
|
|
2020-03-18 13:08:25 +00:00
|
|
|
if (!hasPrefix(path, "/"))
|
|
|
|
// FIXME: register as GC root.
|
|
|
|
// FIXME: if download fails, use previous version if available.
|
|
|
|
path = store->toRealPath(downloadFile(store, path, "flake-registry.json", false));
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
return Registry::read(path, Registry::Global);
|
|
|
|
}();
|
|
|
|
|
|
|
|
return reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
Registries getRegistries(ref<Store> store)
|
|
|
|
{
|
|
|
|
Registries registries;
|
2020-01-22 19:00:58 +00:00
|
|
|
registries.push_back(getFlagRegistry());
|
2020-01-21 15:27:53 +00:00
|
|
|
registries.push_back(getUserRegistry());
|
|
|
|
registries.push_back(getGlobalRegistry(store));
|
|
|
|
return registries;
|
|
|
|
}
|
|
|
|
|
2020-03-17 19:54:36 +00:00
|
|
|
std::pair<std::shared_ptr<const Input>, Attrs> lookupInRegistries(
|
2020-01-21 15:27:53 +00:00
|
|
|
ref<Store> store,
|
|
|
|
std::shared_ptr<const Input> input)
|
|
|
|
{
|
2020-03-17 19:54:36 +00:00
|
|
|
Attrs extraAttrs;
|
2020-01-21 15:27:53 +00:00
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
restart:
|
|
|
|
|
|
|
|
n++;
|
|
|
|
if (n > 100) throw Error("cycle detected in flake registr for '%s'", input);
|
|
|
|
|
|
|
|
for (auto & registry : getRegistries(store)) {
|
|
|
|
// FIXME: O(n)
|
|
|
|
for (auto & entry : registry->entries) {
|
2020-02-20 21:14:44 +00:00
|
|
|
auto from = std::get<0>(entry);
|
|
|
|
if (from->contains(*input)) {
|
|
|
|
input = std::get<1>(entry)->applyOverrides(
|
|
|
|
!from->getRef() && input->getRef() ? input->getRef() : std::optional<std::string>(),
|
|
|
|
!from->getRev() && input->getRev() ? input->getRev() : std::optional<Hash>());
|
|
|
|
extraAttrs = std::get<2>(entry);
|
2020-01-21 15:27:53 +00:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!input->isDirect())
|
|
|
|
throw Error("cannot find flake '%s' in the flake registries", input->to_string());
|
|
|
|
|
2020-02-20 21:14:44 +00:00
|
|
|
return {input, extraAttrs};
|
2020-01-21 15:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|