diff options
Diffstat (limited to 'src/nix/flake.cc')
-rw-r--r-- | src/nix/flake.cc | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/src/nix/flake.cc b/src/nix/flake.cc new file mode 100644 index 000000000..7836f0cfe --- /dev/null +++ b/src/nix/flake.cc @@ -0,0 +1,422 @@ +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "progress-bar.hh" +#include "eval.hh" +#include "primops/flake.hh" + +#include <nlohmann/json.hpp> +#include <queue> +#include <iomanip> + +using namespace nix; + +class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions +{ + std::string flakeUri = "."; + +public: + + FlakeCommand() + { + expectArg("flake-uri", &flakeUri, true); + } + + FlakeRef getFlakeRef() + { + if (flakeUri.find('/') != std::string::npos || flakeUri == ".") + return FlakeRef(flakeUri, true); + else + return FlakeRef(flakeUri); + } + + Flake getFlake() + { + auto evalState = getEvalState(); + return nix::getFlake(*evalState, getFlakeRef(), useRegistries); + } + + ResolvedFlake resolveFlake() + { + return nix::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); + } +}; + +struct CmdFlakeList : EvalCommand +{ + std::string name() override + { + return "list"; + } + + std::string description() override + { + return "list available Nix flakes"; + } + + void run(nix::ref<nix::Store> store) override + { + auto registries = getEvalState()->getFlakeRegistries(); + + stopProgressBar(); + + for (auto & entry : registries[FLAG_REGISTRY]->entries) + std::cout << entry.first.to_string() << " flags " << entry.second.to_string() << "\n"; + + for (auto & entry : registries[USER_REGISTRY]->entries) + std::cout << entry.first.to_string() << " user " << entry.second.to_string() << "\n"; + + for (auto & entry : registries[GLOBAL_REGISTRY]->entries) + std::cout << entry.first.to_string() << " global " << entry.second.to_string() << "\n"; + } +}; + +static void printSourceInfo(const SourceInfo & sourceInfo) +{ + std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string()); + if (sourceInfo.resolvedRef.ref) + std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); + if (sourceInfo.resolvedRef.rev) + std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false)); + if (sourceInfo.revCount) + std::cout << fmt("Revisions: %s\n", *sourceInfo.revCount); + if (sourceInfo.lastModified) + std::cout << fmt("Last modified: %s\n", + std::put_time(std::localtime(&*sourceInfo.lastModified), "%F %T")); + std::cout << fmt("Path: %s\n", sourceInfo.storePath); +} + +static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) +{ + j["uri"] = sourceInfo.resolvedRef.to_string(); + if (sourceInfo.resolvedRef.ref) + j["branch"] = *sourceInfo.resolvedRef.ref; + if (sourceInfo.resolvedRef.rev) + j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false); + if (sourceInfo.revCount) + j["revCount"] = *sourceInfo.revCount; + if (sourceInfo.lastModified) + j["lastModified"] = *sourceInfo.lastModified; + j["path"] = sourceInfo.storePath; +} + +static void printFlakeInfo(const Flake & flake) +{ + std::cout << fmt("ID: %s\n", flake.id); + std::cout << fmt("Description: %s\n", flake.description); + std::cout << fmt("Epoch: %s\n", flake.epoch); + printSourceInfo(flake.sourceInfo); +} + +static nlohmann::json flakeToJson(const Flake & flake) +{ + nlohmann::json j; + j["id"] = flake.id; + j["description"] = flake.description; + j["epoch"] = flake.epoch; + sourceInfoToJson(flake.sourceInfo, j); + return j; +} + +static void printNonFlakeInfo(const NonFlake & nonFlake) +{ + std::cout << fmt("ID: %s\n", nonFlake.alias); + printSourceInfo(nonFlake.sourceInfo); +} + +static nlohmann::json nonFlakeToJson(const NonFlake & nonFlake) +{ + nlohmann::json j; + j["id"] = nonFlake.alias; + sourceInfoToJson(nonFlake.sourceInfo, j); + return j; +} + +// FIXME: merge info CmdFlakeInfo? +struct CmdFlakeDeps : FlakeCommand +{ + std::string name() override + { + return "deps"; + } + + std::string description() override + { + return "list informaton about dependencies"; + } + + void run(nix::ref<nix::Store> store) override + { + auto evalState = getEvalState(); + evalState->addRegistryOverrides(registryOverrides); + + std::queue<ResolvedFlake> todo; + todo.push(resolveFlake()); + + stopProgressBar(); + + while (!todo.empty()) { + auto resFlake = std::move(todo.front()); + todo.pop(); + + for (auto & nonFlake : resFlake.nonFlakeDeps) + printNonFlakeInfo(nonFlake); + + for (auto & info : resFlake.flakeDeps) { + printFlakeInfo(info.second.flake); + todo.push(info.second); + } + } + } +}; + +struct CmdFlakeUpdate : FlakeCommand +{ + std::string name() override + { + return "update"; + } + + std::string description() override + { + return "update flake lock file"; + } + + void run(nix::ref<nix::Store> store) override + { + auto evalState = getEvalState(); + + auto flakeRef = getFlakeRef(); + + if (std::get_if<FlakeRef::IsPath>(&flakeRef.data)) + updateLockFile(*evalState, flakeRef, true); + else + throw Error("cannot update lockfile of flake '%s'", flakeRef); + } +}; + +struct CmdFlakeInfo : FlakeCommand, MixJSON +{ + std::string name() override + { + return "info"; + } + + std::string description() override + { + return "list info about a given flake"; + } + + CmdFlakeInfo () { } + + void run(nix::ref<nix::Store> store) override + { + auto flake = getFlake(); + stopProgressBar(); + if (json) + std::cout << flakeToJson(flake).dump() << std::endl; + else + printFlakeInfo(flake); + } +}; + +struct CmdFlakeAdd : MixEvalArgs, Command +{ + FlakeUri alias; + FlakeUri uri; + + std::string name() override + { + return "add"; + } + + std::string description() override + { + return "upsert flake in user flake registry"; + } + + CmdFlakeAdd() + { + expectArg("alias", &alias); + expectArg("flake-uri", &uri); + } + + void run() override + { + FlakeRef aliasRef(alias); + Path userRegistryPath = getUserRegistryPath(); + auto userRegistry = readRegistry(userRegistryPath); + userRegistry->entries.erase(aliasRef); + userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(uri)); + writeRegistry(*userRegistry, userRegistryPath); + } +}; + +struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command +{ + FlakeUri alias; + + std::string name() override + { + return "remove"; + } + + std::string description() override + { + return "remove flake from user flake registry"; + } + + CmdFlakeRemove() + { + expectArg("alias", &alias); + } + + void run() override + { + Path userRegistryPath = getUserRegistryPath(); + auto userRegistry = readRegistry(userRegistryPath); + userRegistry->entries.erase(FlakeRef(alias)); + writeRegistry(*userRegistry, userRegistryPath); + } +}; + +struct CmdFlakePin : virtual Args, EvalCommand +{ + FlakeUri alias; + + std::string name() override + { + return "pin"; + } + + std::string description() override + { + return "pin flake require in user flake registry"; + } + + CmdFlakePin() + { + expectArg("alias", &alias); + } + + void run(nix::ref<nix::Store> store) override + { + auto evalState = getEvalState(); + + Path userRegistryPath = getUserRegistryPath(); + FlakeRegistry userRegistry = *readRegistry(userRegistryPath); + auto it = userRegistry.entries.find(FlakeRef(alias)); + if (it != userRegistry.entries.end()) { + it->second = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; + writeRegistry(userRegistry, userRegistryPath); + } else { + std::shared_ptr<FlakeRegistry> globalReg = evalState->getGlobalFlakeRegistry(); + it = globalReg->entries.find(FlakeRef(alias)); + if (it != globalReg->entries.end()) { + auto newRef = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; + userRegistry.entries.insert_or_assign(alias, newRef); + writeRegistry(userRegistry, userRegistryPath); + } else + throw Error("the flake alias '%s' does not exist in the user or global registry", alias); + } + } +}; + +struct CmdFlakeInit : virtual Args, Command +{ + std::string name() override + { + return "init"; + } + + std::string description() override + { + return "create a skeleton 'flake.nix' file in the current directory"; + } + + void run() override + { + Path flakeDir = absPath("."); + + if (!pathExists(flakeDir + "/.git")) + throw Error("the directory '%s' is not a Git repository", flakeDir); + + Path flakePath = flakeDir + "/flake.nix"; + + if (pathExists(flakePath)) + throw Error("file '%s' already exists", flakePath); + + writeFile(flakePath, +#include "flake-template.nix.gen.hh" + ); + } +}; + +struct CmdFlakeClone : FlakeCommand +{ + Path destDir; + + std::string name() override + { + return "clone"; + } + + std::string description() override + { + return "clone flake repository"; + } + + CmdFlakeClone() + { + expectArg("dest-dir", &destDir, true); + } + + void run(nix::ref<nix::Store> store) override + { + auto evalState = getEvalState(); + + Registries registries = evalState->getFlakeRegistries(); + gitCloneFlake(getFlakeRef().to_string(), *evalState, registries, destDir); + } +}; + +struct CmdFlake : virtual MultiCommand, virtual Command +{ + CmdFlake() + : MultiCommand({make_ref<CmdFlakeList>() + , make_ref<CmdFlakeUpdate>() + , make_ref<CmdFlakeInfo>() + , make_ref<CmdFlakeDeps>() + , make_ref<CmdFlakeAdd>() + , make_ref<CmdFlakeRemove>() + , make_ref<CmdFlakePin>() + , make_ref<CmdFlakeInit>() + , make_ref<CmdFlakeClone>() + }) + { + } + + std::string name() override + { + return "flake"; + } + + std::string description() override + { + return "manage Nix flakes"; + } + + void run() override + { + if (!command) + throw UsageError("'nix flake' requires a sub-command."); + command->run(); + } + + void printHelp(const string & programName, std::ostream & out) override + { + MultiCommand::printHelp(programName, out); + } +}; + +static RegisterCommand r1(make_ref<CmdFlake>()); |