Merge pull request #9069 from obsidiansystems/libfetchers-prep-0

`libfetchers` improvements without `libflake`
This commit is contained in:
John Ericson 2023-09-29 10:39:45 -04:00 committed by GitHub
commit 461902b860
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 53 additions and 23 deletions

View file

@ -289,4 +289,6 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)}; return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)};
} }
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
} }

View file

@ -6,6 +6,7 @@
#include "fetchers.hh" #include "fetchers.hh"
#include "outputs-spec.hh" #include "outputs-spec.hh"
#include <regex>
#include <variant> #include <variant>
namespace nix { namespace nix {
@ -91,5 +92,7 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
bool allowMissing = false, bool allowMissing = false,
bool isFlake = true); bool isFlake = true);
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
extern std::regex flakeIdRegex;
} }

View file

@ -195,7 +195,6 @@ static void fetchTree(
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
experimentalFeatureSettings.require(Xp::Flakes);
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
} }
@ -237,12 +236,9 @@ static RegisterPrimOp primop_fetchTree({
```nix ```nix
builtins.fetchTree "https://example.com/" builtins.fetchTree "https://example.com/"
``` ```
> **Note**
>
> This function requires the [`flakes` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-flakes) to be enabled.
)", )",
.fun = prim_fetchTree .fun = prim_fetchTree,
.experimentalFeature = Xp::Flakes,
}); });
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v, static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,

View file

@ -13,6 +13,12 @@
namespace nix::fetchers { namespace nix::fetchers {
typedef std::variant<std::string, uint64_t, Explicit<bool>> Attr; typedef std::variant<std::string, uint64_t, Explicit<bool>> Attr;
/**
* An `Attrs` can be thought of a JSON object restricted or simplified
* to be "flat", not containing any subcontainers (arrays or objects)
* and also not containing any `null`s.
*/
typedef std::map<std::string, Attr> Attrs; typedef std::map<std::string, Attr> Attrs;
Attrs jsonToAttrs(const nlohmann::json & json); Attrs jsonToAttrs(const nlohmann::json & json);

View file

@ -36,6 +36,7 @@ Input Input::fromURL(const ParsedURL & url, bool requireTree)
for (auto & inputScheme : *inputSchemes) { for (auto & inputScheme : *inputSchemes) {
auto res = inputScheme->inputFromURL(url, requireTree); auto res = inputScheme->inputFromURL(url, requireTree);
if (res) { if (res) {
experimentalFeatureSettings.require(inputScheme->experimentalFeature());
res->scheme = inputScheme; res->scheme = inputScheme;
fixupInput(*res); fixupInput(*res);
return std::move(*res); return std::move(*res);
@ -50,6 +51,7 @@ Input Input::fromAttrs(Attrs && attrs)
for (auto & inputScheme : *inputSchemes) { for (auto & inputScheme : *inputSchemes) {
auto res = inputScheme->inputFromAttrs(attrs); auto res = inputScheme->inputFromAttrs(attrs);
if (res) { if (res) {
experimentalFeatureSettings.require(inputScheme->experimentalFeature());
res->scheme = inputScheme; res->scheme = inputScheme;
fixupInput(*res); fixupInput(*res);
return std::move(*res); return std::move(*res);
@ -254,7 +256,8 @@ std::optional<Hash> Input::getRev() const
try { try {
hash = Hash::parseAnyPrefixed(*s); hash = Hash::parseAnyPrefixed(*s);
} catch (BadHash &e) { } catch (BadHash &e) {
// Default to sha1 for backwards compatibility with existing flakes // Default to sha1 for backwards compatibility with existing
// usages (e.g. `builtins.fetchTree` calls or flake inputs).
hash = Hash::parseAny(*s, htSHA1); hash = Hash::parseAny(*s, htSHA1);
} }
} }
@ -308,4 +311,9 @@ void InputScheme::clone(const Input & input, const Path & destDir) const
throw Error("do not know how to clone input '%s'", input.to_string()); throw Error("do not know how to clone input '%s'", input.to_string());
} }
std::optional<ExperimentalFeature> InputScheme::experimentalFeature()
{
return {};
}
} }

View file

@ -22,12 +22,11 @@ struct Tree
struct InputScheme; struct InputScheme;
/** /**
* The Input object is generated by a specific fetcher, based on the * The `Input` object is generated by a specific fetcher, based on
* user-supplied input attribute in the flake.nix file, and contains * user-supplied information, and contains
* the information that the specific fetcher needs to perform the * the information that the specific fetcher needs to perform the
* actual fetch. The Input object is most commonly created via the * actual fetch. The Input object is most commonly created via the
* "fromURL()" or "fromAttrs()" static functions which are provided * `fromURL()` or `fromAttrs()` static functions.
* the url or attrset specified in the flake file.
*/ */
struct Input struct Input
{ {
@ -44,10 +43,20 @@ struct Input
std::optional<Path> parent; std::optional<Path> parent;
public: public:
/**
* Create an `Input` from a URL.
*
* The URL indicate which sort of fetcher, and provides information to that fetcher.
*/
static Input fromURL(const std::string & url, bool requireTree = true); static Input fromURL(const std::string & url, bool requireTree = true);
static Input fromURL(const ParsedURL & url, bool requireTree = true); static Input fromURL(const ParsedURL & url, bool requireTree = true);
/**
* Create an `Input` from a an `Attrs`.
*
* The URL indicate which sort of fetcher, and provides information to that fetcher.
*/
static Input fromAttrs(Attrs && attrs); static Input fromAttrs(Attrs && attrs);
ParsedURL toURL() const; ParsedURL toURL() const;
@ -116,13 +125,13 @@ public:
/** /**
* The InputScheme represents a type of fetcher. Each fetcher * The `InputScheme` represents a type of fetcher. Each fetcher
* registers with nix at startup time. When processing an input for a * registers with nix at startup time. When processing an `Input`,
* flake, each scheme is given an opportunity to "recognize" that * each scheme is given an opportunity to "recognize" that
* input from the url or attributes in the flake file's specification * input from the user-provided url or attributes
* and return an Input object to represent the input if it is * and return an `Input` object to represent the input if it is
* recognized. The Input object contains the information the fetcher * recognized. The `Input` object contains the information the fetcher
* needs to actually perform the "fetch()" when called. * needs to actually perform the `fetch()` when called.
*/ */
struct InputScheme struct InputScheme
{ {
@ -149,6 +158,11 @@ struct InputScheme
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg); virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0; virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
/**
* Is this `InputScheme` part of an experimental feature?
*/
virtual std::optional<ExperimentalFeature> experimentalFeature();
}; };
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher); void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);

View file

@ -98,6 +98,11 @@ struct IndirectInputScheme : InputScheme
{ {
throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
} }
std::optional<ExperimentalFeature> experimentalFeature() override
{
return Xp::Flakes;
}
}; };
static auto rIndirectInputScheme = OnStartup([] { registerInputScheme(std::make_unique<IndirectInputScheme>()); }); static auto rIndirectInputScheme = OnStartup([] { registerInputScheme(std::make_unique<IndirectInputScheme>()); });

View file

@ -236,7 +236,7 @@ struct Command : virtual public Args
static constexpr Category catDefault = 0; static constexpr Category catDefault = 0;
virtual std::optional<ExperimentalFeature> experimentalFeature (); virtual std::optional<ExperimentalFeature> experimentalFeature();
virtual Category category() { return catDefault; } virtual Category category() { return catDefault; }
}; };

View file

@ -41,7 +41,4 @@ extern std::regex revRegex;
/// A ref or revision, or a ref followed by a revision. /// A ref or revision, or a ref followed by a revision.
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))"; const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
extern std::regex flakeIdRegex;
} }

View file

@ -8,7 +8,6 @@ namespace nix {
std::regex refRegex(refRegexS, std::regex::ECMAScript); std::regex refRegex(refRegexS, std::regex::ECMAScript);
std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript); std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript);
std::regex revRegex(revRegexS, std::regex::ECMAScript); std::regex revRegex(revRegexS, std::regex::ECMAScript);
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
ParsedURL parseURL(const std::string & url) ParsedURL parseURL(const std::string & url)
{ {