aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libfetchers/tarball.cc90
-rw-r--r--src/libutil/url.cc18
-rw-r--r--src/libutil/url.hh15
-rw-r--r--src/nix/flake.md14
4 files changed, 112 insertions, 25 deletions
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index dde0ad761..09acb74d3 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -6,6 +6,7 @@
#include "archive.hh"
#include "tarfile.hh"
#include "types.hh"
+#include "split.hh"
namespace nix::fetchers {
@@ -168,24 +169,34 @@ std::pair<Tree, time_t> downloadTarball(
};
}
-struct TarballInputScheme : InputScheme
+// An input scheme corresponding to a curable ressource
+struct CurlInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) override
+ virtual const std::string inputType() const = 0;
+ const std::set<std::string> transportUrlSchemes = {"file", "http", "https"};
+
+ const bool hasTarballExtension(std::string_view path) const
{
- if (url.scheme != "file" && url.scheme != "http" && url.scheme != "https") return {};
+ return hasSuffix(path, ".zip") || hasSuffix(path, ".tar")
+ || hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz")
+ || hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2")
+ || hasSuffix(path, ".tar.zst");
+ }
- if (!hasSuffix(url.path, ".zip")
- && !hasSuffix(url.path, ".tar")
- && !hasSuffix(url.path, ".tgz")
- && !hasSuffix(url.path, ".tar.gz")
- && !hasSuffix(url.path, ".tar.xz")
- && !hasSuffix(url.path, ".tar.bz2")
- && !hasSuffix(url.path, ".tar.zst"))
- return {};
+ virtual bool isValidURL(const ParsedURL & url) const = 0;
+
+ std::optional<Input> inputFromURL(const ParsedURL & url) override
+ {
+ if (!isValidURL(url))
+ return std::nullopt;
Input input;
- input.attrs.insert_or_assign("type", "tarball");
- input.attrs.insert_or_assign("url", url.to_string());
+
+ auto urlWithoutApplicationScheme = url;
+ urlWithoutApplicationScheme.scheme = parseUrlScheme(url.scheme).transport;
+
+ input.attrs.insert_or_assign("type", inputType());
+ input.attrs.insert_or_assign("url", urlWithoutApplicationScheme.to_string());
auto narHash = url.query.find("narHash");
if (narHash != url.query.end())
input.attrs.insert_or_assign("narHash", narHash->second);
@@ -194,14 +205,17 @@ struct TarballInputScheme : InputScheme
std::optional<Input> inputFromAttrs(const Attrs & attrs) override
{
- if (maybeGetStrAttr(attrs, "type") != "tarball") return {};
+ auto type = maybeGetStrAttr(attrs, "type");
+ if (type != inputType()) return {};
+ std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack"};
for (auto & [name, value] : attrs)
- if (name != "type" && name != "url" && /* name != "hash" && */ name != "narHash" && name != "name")
- throw Error("unsupported tarball input attribute '%s'", name);
+ if (!allowedNames.count(name))
+ throw Error("unsupported %s input attribute '%s'", *type, name);
Input input;
input.attrs = attrs;
+
//input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
return input;
}
@@ -209,14 +223,9 @@ struct TarballInputScheme : InputScheme
ParsedURL toURL(const Input & input) override
{
auto url = parseURL(getStrAttr(input.attrs, "url"));
- // NAR hashes are preferred over file hashes since tar/zip files
- // don't have a canonical representation.
+ // NAR hashes are preferred over file hashes since tar/zip files // don't have a canonical representation.
if (auto narHash = input.getNarHash())
url.query.insert_or_assign("narHash", narHash->to_string(SRI, true));
- /*
- else if (auto hash = maybeGetStrAttr(input.attrs, "hash"))
- url.query.insert_or_assign("hash", Hash(*hash).to_string(SRI, true));
- */
return url;
}
@@ -225,6 +234,42 @@ struct TarballInputScheme : InputScheme
return true;
}
+};
+
+struct FileInputScheme : CurlInputScheme
+{
+ const std::string inputType() const override { return "file"; }
+
+ bool isValidURL(const ParsedURL & url) const override
+ {
+ auto parsedUrlScheme = parseUrlScheme(url.scheme);
+ return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
+ && (parsedUrlScheme.application
+ ? parsedUrlScheme.application.value() == inputType()
+ : !hasTarballExtension(url.path));
+ }
+
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
+ {
+ auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false);
+ return {std::move(file.storePath), input};
+ }
+};
+
+struct TarballInputScheme : CurlInputScheme
+{
+ const std::string inputType() const override { return "tarball"; }
+
+ bool isValidURL(const ParsedURL & url) const override
+ {
+ auto parsedUrlScheme = parseUrlScheme(url.scheme);
+
+ return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
+ && (parsedUrlScheme.application
+ ? parsedUrlScheme.application.value() == inputType()
+ : hasTarballExtension(url.path));
+ }
+
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
{
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
@@ -233,5 +278,6 @@ struct TarballInputScheme : InputScheme
};
static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique<TarballInputScheme>()); });
+static auto rFileInputScheme = OnStartup([] { registerInputScheme(std::make_unique<FileInputScheme>()); });
}
diff --git a/src/libutil/url.cc b/src/libutil/url.cc
index f6232d255..5b7abeb49 100644
--- a/src/libutil/url.cc
+++ b/src/libutil/url.cc
@@ -1,6 +1,7 @@
#include "url.hh"
#include "url-parts.hh"
#include "util.hh"
+#include "split.hh"
namespace nix {
@@ -136,4 +137,21 @@ bool ParsedURL::operator ==(const ParsedURL & other) const
&& fragment == other.fragment;
}
+/**
+ * Parse a URL scheme of the form '(applicationScheme\+)?transportScheme'
+ * into a tuple '(applicationScheme, transportScheme)'
+ *
+ * > parseUrlScheme("http") == ParsedUrlScheme{ {}, "http"}
+ * > parseUrlScheme("tarball+http") == ParsedUrlScheme{ {"tarball"}, "http"}
+ */
+ParsedUrlScheme parseUrlScheme(std::string_view scheme)
+{
+ auto application = splitPrefixTo(scheme, '+');
+ auto transport = scheme;
+ return ParsedUrlScheme {
+ .application = application,
+ .transport = transport,
+ };
+}
+
}
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index 6e77142e3..2a9fb34c1 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -27,4 +27,19 @@ std::map<std::string, std::string> decodeQuery(const std::string & query);
ParsedURL parseURL(const std::string & url);
+/*
+ * Although that’s not really standardized anywhere, an number of tools
+ * use a scheme of the form 'x+y' in urls, where y is the “transport layer”
+ * scheme, and x is the “application layer” scheme.
+ *
+ * For example git uses `git+https` to designate remotes using a Git
+ * protocol over http.
+ */
+struct ParsedUrlScheme {
+ std::optional<std::string_view> application;
+ std::string_view transport;
+};
+
+ParsedUrlScheme parseUrlScheme(std::string_view scheme);
+
}
diff --git a/src/nix/flake.md b/src/nix/flake.md
index aa3f9f303..a1ab43281 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -181,9 +181,17 @@ Currently the `type` attribute can be one of the following:
* `tarball`: Tarballs. The location of the tarball is specified by the
attribute `url`.
- In URL form, the schema must be `http://`, `https://` or `file://`
- URLs and the extension must be `.zip`, `.tar`, `.tgz`, `.tar.gz`,
- `.tar.xz`, `.tar.bz2` or `.tar.zst`.
+ In URL form, the schema must be `tarball+http://`, `tarball+https://` or `tarball+file://`.
+ If the extension corresponds to a known archive format (`.zip`, `.tar`,
+ `.tgz`, `.tar.gz`, `.tar.xz`, `.tar.bz2` or `.tar.zst`), then the `tarball+`
+ can be dropped.
+
+* `file`: Plain files or directory tarballs, either over http(s) or from the local
+ disk.
+
+ In URL form, the schema must be `file+http://`, `file+https://` or `file+file://`.
+ If the extension doesn’t correspond to a known archive format (as defined by the
+ `tarball` fetcher), then the `file+` prefix can be dropped.
* `github`: A more efficient way to fetch repositories from
GitHub. The following attributes are required: