2020-03-30 14:04:18 +00:00
|
|
|
#include "fetchers.hh"
|
|
|
|
#include "cache.hh"
|
2020-04-08 12:12:22 +00:00
|
|
|
#include "filetransfer.hh"
|
2020-03-30 14:04:18 +00:00
|
|
|
#include "globals.hh"
|
|
|
|
#include "store-api.hh"
|
|
|
|
#include "archive.hh"
|
|
|
|
#include "tarfile.hh"
|
2020-06-17 19:08:59 +00:00
|
|
|
#include "types.hh"
|
2020-10-15 23:35:24 +00:00
|
|
|
#include "split.hh"
|
2020-03-30 14:04:18 +00:00
|
|
|
|
|
|
|
namespace nix::fetchers {
|
|
|
|
|
|
|
|
DownloadFileResult downloadFile(
|
|
|
|
ref<Store> store,
|
|
|
|
const std::string & url,
|
|
|
|
const std::string & name,
|
2022-02-24 17:09:00 +00:00
|
|
|
bool locked,
|
2020-09-29 11:05:19 +00:00
|
|
|
const Headers & headers)
|
2020-03-30 14:04:18 +00:00
|
|
|
{
|
|
|
|
// FIXME: check store
|
|
|
|
|
|
|
|
Attrs inAttrs({
|
|
|
|
{"type", "file"},
|
|
|
|
{"url", url},
|
|
|
|
{"name", name},
|
|
|
|
});
|
|
|
|
|
|
|
|
auto cached = getCache()->lookupExpired(store, inAttrs);
|
|
|
|
|
|
|
|
auto useCached = [&]() -> DownloadFileResult
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
.storePath = std::move(cached->storePath),
|
|
|
|
.etag = getStrAttr(cached->infoAttrs, "etag"),
|
2023-06-07 12:26:30 +00:00
|
|
|
.effectiveUrl = getStrAttr(cached->infoAttrs, "url"),
|
|
|
|
.immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"),
|
2020-03-30 14:04:18 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
if (cached && !cached->expired)
|
|
|
|
return useCached();
|
|
|
|
|
2020-09-29 11:05:19 +00:00
|
|
|
FileTransferRequest request(url);
|
|
|
|
request.headers = headers;
|
2020-03-30 14:04:18 +00:00
|
|
|
if (cached)
|
|
|
|
request.expectedETag = getStrAttr(cached->infoAttrs, "etag");
|
2020-04-08 12:12:22 +00:00
|
|
|
FileTransferResult res;
|
2020-03-30 14:04:18 +00:00
|
|
|
try {
|
2024-05-01 16:01:19 +00:00
|
|
|
res = getFileTransfer()->transfer(request);
|
2020-04-08 12:12:22 +00:00
|
|
|
} catch (FileTransferError & e) {
|
2020-03-30 14:04:18 +00:00
|
|
|
if (cached) {
|
|
|
|
warn("%s; using cached version", e.msg());
|
|
|
|
return useCached();
|
|
|
|
} else
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: write to temporary file.
|
|
|
|
Attrs infoAttrs({
|
|
|
|
{"etag", res.etag},
|
|
|
|
{"url", res.effectiveUri},
|
|
|
|
});
|
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
if (res.immutableUrl)
|
|
|
|
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
|
|
|
|
|
2020-03-30 14:04:18 +00:00
|
|
|
std::optional<StorePath> storePath;
|
|
|
|
|
|
|
|
if (res.cached) {
|
|
|
|
assert(cached);
|
|
|
|
storePath = std::move(cached->storePath);
|
|
|
|
} else {
|
|
|
|
StringSink sink;
|
2022-01-17 21:20:05 +00:00
|
|
|
dumpString(res.data, sink);
|
|
|
|
auto hash = hashString(htSHA256, res.data);
|
2020-08-06 18:31:48 +00:00
|
|
|
ValidPathInfo info {
|
2020-10-07 13:52:20 +00:00
|
|
|
*store,
|
2023-01-23 17:58:11 +00:00
|
|
|
name,
|
|
|
|
FixedOutputInfo {
|
2023-07-05 22:53:44 +00:00
|
|
|
.method = FileIngestionMethod::Flat,
|
|
|
|
.hash = hash,
|
2023-02-28 16:57:20 +00:00
|
|
|
.references = {},
|
2020-10-07 13:52:20 +00:00
|
|
|
},
|
2022-01-17 21:20:05 +00:00
|
|
|
hashString(htSHA256, sink.s),
|
2020-08-06 18:31:48 +00:00
|
|
|
};
|
2022-01-17 21:20:05 +00:00
|
|
|
info.narSize = sink.s.size();
|
2022-03-10 15:48:14 +00:00
|
|
|
auto source = StringSource { sink.s };
|
2020-05-29 20:19:48 +00:00
|
|
|
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
2020-03-30 14:04:18 +00:00
|
|
|
storePath = std::move(info.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
getCache()->add(
|
|
|
|
store,
|
|
|
|
inAttrs,
|
|
|
|
infoAttrs,
|
|
|
|
*storePath,
|
2022-02-24 17:09:00 +00:00
|
|
|
locked);
|
2020-03-30 14:04:18 +00:00
|
|
|
|
|
|
|
if (url != res.effectiveUri)
|
|
|
|
getCache()->add(
|
|
|
|
store,
|
|
|
|
{
|
|
|
|
{"type", "file"},
|
|
|
|
{"url", res.effectiveUri},
|
|
|
|
{"name", name},
|
|
|
|
},
|
|
|
|
infoAttrs,
|
|
|
|
*storePath,
|
2022-02-24 17:09:00 +00:00
|
|
|
locked);
|
2020-03-30 14:04:18 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
.storePath = std::move(*storePath),
|
|
|
|
.etag = res.etag,
|
|
|
|
.effectiveUrl = res.effectiveUri,
|
2023-06-07 12:26:30 +00:00
|
|
|
.immutableUrl = res.immutableUrl,
|
2020-03-30 14:04:18 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
DownloadTarballResult downloadTarball(
|
2020-03-30 14:04:18 +00:00
|
|
|
ref<Store> store,
|
|
|
|
const std::string & url,
|
|
|
|
const std::string & name,
|
2022-02-24 17:09:00 +00:00
|
|
|
bool locked,
|
2020-09-29 11:05:19 +00:00
|
|
|
const Headers & headers)
|
2020-03-30 14:04:18 +00:00
|
|
|
{
|
|
|
|
Attrs inAttrs({
|
|
|
|
{"type", "tarball"},
|
|
|
|
{"url", url},
|
|
|
|
{"name", name},
|
|
|
|
});
|
|
|
|
|
|
|
|
auto cached = getCache()->lookupExpired(store, inAttrs);
|
|
|
|
|
|
|
|
if (cached && !cached->expired)
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
return {
|
2023-06-07 12:26:30 +00:00
|
|
|
.tree = Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
|
|
|
|
.lastModified = (time_t) getIntAttr(cached->infoAttrs, "lastModified"),
|
|
|
|
.immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"),
|
2020-03-30 14:04:18 +00:00
|
|
|
};
|
|
|
|
|
2022-02-24 17:09:00 +00:00
|
|
|
auto res = downloadFile(store, url, name, locked, headers);
|
2020-03-30 14:04:18 +00:00
|
|
|
|
|
|
|
std::optional<StorePath> unpackedStorePath;
|
|
|
|
time_t lastModified;
|
|
|
|
|
|
|
|
if (cached && res.etag != "" && getStrAttr(cached->infoAttrs, "etag") == res.etag) {
|
|
|
|
unpackedStorePath = std::move(cached->storePath);
|
|
|
|
lastModified = getIntAttr(cached->infoAttrs, "lastModified");
|
|
|
|
} else {
|
|
|
|
Path tmpDir = createTempDir();
|
|
|
|
AutoDelete autoDelete(tmpDir, true);
|
|
|
|
unpackTarfile(store->toRealPath(res.storePath), tmpDir);
|
|
|
|
auto members = readDirectory(tmpDir);
|
|
|
|
if (members.size() != 1)
|
|
|
|
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
|
|
|
auto topDir = tmpDir + "/" + members.begin()->name;
|
|
|
|
lastModified = lstat(topDir).st_mtime;
|
2020-05-26 15:32:41 +00:00
|
|
|
unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, NoRepair);
|
2020-03-30 14:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Attrs infoAttrs({
|
2020-12-08 20:16:06 +00:00
|
|
|
{"lastModified", uint64_t(lastModified)},
|
2020-03-30 14:04:18 +00:00
|
|
|
{"etag", res.etag},
|
|
|
|
});
|
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
if (res.immutableUrl)
|
|
|
|
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
|
|
|
|
|
2020-03-30 14:04:18 +00:00
|
|
|
getCache()->add(
|
|
|
|
store,
|
|
|
|
inAttrs,
|
|
|
|
infoAttrs,
|
|
|
|
*unpackedStorePath,
|
2022-02-24 17:09:00 +00:00
|
|
|
locked);
|
2020-03-30 14:04:18 +00:00
|
|
|
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
return {
|
2023-06-07 12:26:30 +00:00
|
|
|
.tree = Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
|
|
|
|
.lastModified = lastModified,
|
|
|
|
.immutableUrl = res.immutableUrl,
|
2020-03-30 14:04:18 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-05-25 13:29:27 +00:00
|
|
|
// An input scheme corresponding to a curl-downloadable resource.
|
2020-10-15 23:35:24 +00:00
|
|
|
struct CurlInputScheme : InputScheme
|
2020-03-30 14:04:18 +00:00
|
|
|
{
|
2020-10-15 23:35:24 +00:00
|
|
|
virtual const std::string inputType() const = 0;
|
|
|
|
const std::set<std::string> transportUrlSchemes = {"file", "http", "https"};
|
|
|
|
|
2024-05-09 13:00:51 +00:00
|
|
|
bool hasTarballExtension(std::string_view path) const
|
2020-03-30 14:04:18 +00:00
|
|
|
{
|
2024-03-18 02:14:18 +00:00
|
|
|
return path.ends_with(".zip") || path.ends_with(".tar")
|
|
|
|
|| path.ends_with(".tgz") || path.ends_with(".tar.gz")
|
|
|
|
|| path.ends_with(".tar.xz") || path.ends_with(".tar.bz2")
|
|
|
|
|| path.ends_with(".tar.zst");
|
2020-10-15 23:35:24 +00:00
|
|
|
}
|
2020-03-30 14:04:18 +00:00
|
|
|
|
2023-08-01 14:07:20 +00:00
|
|
|
virtual bool isValidURL(const ParsedURL & url, bool requireTree) const = 0;
|
2020-10-15 23:35:24 +00:00
|
|
|
|
2023-08-01 14:07:20 +00:00
|
|
|
std::optional<Input> inputFromURL(const ParsedURL & _url, bool requireTree) const override
|
2020-10-15 23:35:24 +00:00
|
|
|
{
|
2023-08-01 14:07:20 +00:00
|
|
|
if (!isValidURL(_url, requireTree))
|
2020-10-15 23:35:24 +00:00
|
|
|
return std::nullopt;
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
|
Revert "libfetchers: make attribute / URL query handling consistent"
This reverts commit 35eec921af1043fc6322edc0ad88c872d41623b8.
Reason for revert: Regressed nix-eval-jobs, and it appears to be this change is buggy/missing a case. It just needs another pass.
Code causing the problem in n-e-j, when invoked with `nix-eval-jobs --flake '.#hydraJobs'`:
```
n-e-j/tests/assets » ../../build/src/nix-eval-jobs --meta --workers 1 --flake .#hydraJobs
warning: unknown setting 'trusted-users'
warning: `--gc-roots-dir' not specified
error: unsupported Git input attribute 'dir'
error: worker error: error: unsupported Git input attribute 'dir'
```
```
nix::Value *vRoot = [&]() {
if (args.flake) {
auto [flakeRef, fragment, outputSpec] =
nix::parseFlakeRefWithFragmentAndExtendedOutputsSpec(
args.releaseExpr, nix::absPath("."));
nix::InstallableFlake flake{
{}, state, std::move(flakeRef), fragment, outputSpec,
{}, {}, args.lockFlags};
return flake.toValue(*state).first;
} else {
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
```
Inspecting the program behaviour reveals that `dir` was in fact set in the URL going into the fetcher. This is in turn because unlike in the case changed in this commit, it was not erased before handing it to libfetchers, which is probably just a mistake.
```
(rr) up
3 0x00007ffff60262ae in nix::fetchers::Input::fromURL (url=..., requireTree=requireTree@entry=true) at src/libfetchers/fetchers.cc:39
warning: Source file is more recent than executable.
39 auto res = inputScheme->inputFromURL(url, requireTree);
(rr) p url
$1 = (const nix::ParsedURL &) @0x7fffdc874190: {url = "git+file:///home/jade/lix/nix-eval-jobs",
base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file", authority = std::optional<std::string> = {[contained value] = ""},
path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {["dir"] = "tests/assets"}, fragment = ""}
(rr) up
4 0x00007ffff789d904 in nix::parseFlakeRefWithFragment (url=".#hydraJobs", baseDir=std::optional<std::string> = {...},
allowMissing=allowMissing@entry=false, isFlake=isFlake@entry=true) at src/libexpr/flake/flakeref.cc:179
warning: Source file is more recent than executable.
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
(rr) p parsedURL
$2 = {url = "git+file:///home/jade/lix/nix-eval-jobs", base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file",
authority = std::optional<std::string> = {[contained value] = ""}, path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {
["dir"] = "tests/assets"}, fragment = ""}
(rr) list
174
175 if (pathExists(flakeRoot + "/.git/shallow"))
176 parsedURL.query.insert_or_assign("shallow", "1");
177
178 return std::make_pair(
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
180 fragment);
181 }
```
Change-Id: Ib55a882eaeb3e59228857761dc1e3b2e366b0f5e
2024-06-24 22:49:17 +00:00
|
|
|
Input input;
|
2023-06-07 12:26:30 +00:00
|
|
|
|
Revert "libfetchers: make attribute / URL query handling consistent"
This reverts commit 35eec921af1043fc6322edc0ad88c872d41623b8.
Reason for revert: Regressed nix-eval-jobs, and it appears to be this change is buggy/missing a case. It just needs another pass.
Code causing the problem in n-e-j, when invoked with `nix-eval-jobs --flake '.#hydraJobs'`:
```
n-e-j/tests/assets » ../../build/src/nix-eval-jobs --meta --workers 1 --flake .#hydraJobs
warning: unknown setting 'trusted-users'
warning: `--gc-roots-dir' not specified
error: unsupported Git input attribute 'dir'
error: worker error: error: unsupported Git input attribute 'dir'
```
```
nix::Value *vRoot = [&]() {
if (args.flake) {
auto [flakeRef, fragment, outputSpec] =
nix::parseFlakeRefWithFragmentAndExtendedOutputsSpec(
args.releaseExpr, nix::absPath("."));
nix::InstallableFlake flake{
{}, state, std::move(flakeRef), fragment, outputSpec,
{}, {}, args.lockFlags};
return flake.toValue(*state).first;
} else {
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
```
Inspecting the program behaviour reveals that `dir` was in fact set in the URL going into the fetcher. This is in turn because unlike in the case changed in this commit, it was not erased before handing it to libfetchers, which is probably just a mistake.
```
(rr) up
3 0x00007ffff60262ae in nix::fetchers::Input::fromURL (url=..., requireTree=requireTree@entry=true) at src/libfetchers/fetchers.cc:39
warning: Source file is more recent than executable.
39 auto res = inputScheme->inputFromURL(url, requireTree);
(rr) p url
$1 = (const nix::ParsedURL &) @0x7fffdc874190: {url = "git+file:///home/jade/lix/nix-eval-jobs",
base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file", authority = std::optional<std::string> = {[contained value] = ""},
path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {["dir"] = "tests/assets"}, fragment = ""}
(rr) up
4 0x00007ffff789d904 in nix::parseFlakeRefWithFragment (url=".#hydraJobs", baseDir=std::optional<std::string> = {...},
allowMissing=allowMissing@entry=false, isFlake=isFlake@entry=true) at src/libexpr/flake/flakeref.cc:179
warning: Source file is more recent than executable.
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
(rr) p parsedURL
$2 = {url = "git+file:///home/jade/lix/nix-eval-jobs", base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file",
authority = std::optional<std::string> = {[contained value] = ""}, path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {
["dir"] = "tests/assets"}, fragment = ""}
(rr) list
174
175 if (pathExists(flakeRoot + "/.git/shallow"))
176 parsedURL.query.insert_or_assign("shallow", "1");
177
178 return std::make_pair(
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
180 fragment);
181 }
```
Change-Id: Ib55a882eaeb3e59228857761dc1e3b2e366b0f5e
2024-06-24 22:49:17 +00:00
|
|
|
auto url = _url;
|
2023-06-07 12:26:30 +00:00
|
|
|
|
2024-05-04 10:55:10 +00:00
|
|
|
url.scheme = parseUrlScheme(url.scheme).transport;
|
2023-06-07 12:26:30 +00:00
|
|
|
|
Revert "libfetchers: make attribute / URL query handling consistent"
This reverts commit 35eec921af1043fc6322edc0ad88c872d41623b8.
Reason for revert: Regressed nix-eval-jobs, and it appears to be this change is buggy/missing a case. It just needs another pass.
Code causing the problem in n-e-j, when invoked with `nix-eval-jobs --flake '.#hydraJobs'`:
```
n-e-j/tests/assets » ../../build/src/nix-eval-jobs --meta --workers 1 --flake .#hydraJobs
warning: unknown setting 'trusted-users'
warning: `--gc-roots-dir' not specified
error: unsupported Git input attribute 'dir'
error: worker error: error: unsupported Git input attribute 'dir'
```
```
nix::Value *vRoot = [&]() {
if (args.flake) {
auto [flakeRef, fragment, outputSpec] =
nix::parseFlakeRefWithFragmentAndExtendedOutputsSpec(
args.releaseExpr, nix::absPath("."));
nix::InstallableFlake flake{
{}, state, std::move(flakeRef), fragment, outputSpec,
{}, {}, args.lockFlags};
return flake.toValue(*state).first;
} else {
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
```
Inspecting the program behaviour reveals that `dir` was in fact set in the URL going into the fetcher. This is in turn because unlike in the case changed in this commit, it was not erased before handing it to libfetchers, which is probably just a mistake.
```
(rr) up
3 0x00007ffff60262ae in nix::fetchers::Input::fromURL (url=..., requireTree=requireTree@entry=true) at src/libfetchers/fetchers.cc:39
warning: Source file is more recent than executable.
39 auto res = inputScheme->inputFromURL(url, requireTree);
(rr) p url
$1 = (const nix::ParsedURL &) @0x7fffdc874190: {url = "git+file:///home/jade/lix/nix-eval-jobs",
base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file", authority = std::optional<std::string> = {[contained value] = ""},
path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {["dir"] = "tests/assets"}, fragment = ""}
(rr) up
4 0x00007ffff789d904 in nix::parseFlakeRefWithFragment (url=".#hydraJobs", baseDir=std::optional<std::string> = {...},
allowMissing=allowMissing@entry=false, isFlake=isFlake@entry=true) at src/libexpr/flake/flakeref.cc:179
warning: Source file is more recent than executable.
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
(rr) p parsedURL
$2 = {url = "git+file:///home/jade/lix/nix-eval-jobs", base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file",
authority = std::optional<std::string> = {[contained value] = ""}, path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {
["dir"] = "tests/assets"}, fragment = ""}
(rr) list
174
175 if (pathExists(flakeRoot + "/.git/shallow"))
176 parsedURL.query.insert_or_assign("shallow", "1");
177
178 return std::make_pair(
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
180 fragment);
181 }
```
Change-Id: Ib55a882eaeb3e59228857761dc1e3b2e366b0f5e
2024-06-24 22:49:17 +00:00
|
|
|
auto narHash = url.query.find("narHash");
|
|
|
|
if (narHash != url.query.end())
|
|
|
|
input.attrs.insert_or_assign("narHash", narHash->second);
|
|
|
|
|
|
|
|
if (auto i = get(url.query, "rev"))
|
|
|
|
input.attrs.insert_or_assign("rev", *i);
|
|
|
|
|
|
|
|
if (auto i = get(url.query, "revCount"))
|
|
|
|
if (auto n = string2Int<uint64_t>(*i))
|
|
|
|
input.attrs.insert_or_assign("revCount", *n);
|
|
|
|
|
|
|
|
url.query.erase("rev");
|
|
|
|
url.query.erase("revCount");
|
2023-06-07 12:26:30 +00:00
|
|
|
|
Revert "libfetchers: make attribute / URL query handling consistent"
This reverts commit 35eec921af1043fc6322edc0ad88c872d41623b8.
Reason for revert: Regressed nix-eval-jobs, and it appears to be this change is buggy/missing a case. It just needs another pass.
Code causing the problem in n-e-j, when invoked with `nix-eval-jobs --flake '.#hydraJobs'`:
```
n-e-j/tests/assets » ../../build/src/nix-eval-jobs --meta --workers 1 --flake .#hydraJobs
warning: unknown setting 'trusted-users'
warning: `--gc-roots-dir' not specified
error: unsupported Git input attribute 'dir'
error: worker error: error: unsupported Git input attribute 'dir'
```
```
nix::Value *vRoot = [&]() {
if (args.flake) {
auto [flakeRef, fragment, outputSpec] =
nix::parseFlakeRefWithFragmentAndExtendedOutputsSpec(
args.releaseExpr, nix::absPath("."));
nix::InstallableFlake flake{
{}, state, std::move(flakeRef), fragment, outputSpec,
{}, {}, args.lockFlags};
return flake.toValue(*state).first;
} else {
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
```
Inspecting the program behaviour reveals that `dir` was in fact set in the URL going into the fetcher. This is in turn because unlike in the case changed in this commit, it was not erased before handing it to libfetchers, which is probably just a mistake.
```
(rr) up
3 0x00007ffff60262ae in nix::fetchers::Input::fromURL (url=..., requireTree=requireTree@entry=true) at src/libfetchers/fetchers.cc:39
warning: Source file is more recent than executable.
39 auto res = inputScheme->inputFromURL(url, requireTree);
(rr) p url
$1 = (const nix::ParsedURL &) @0x7fffdc874190: {url = "git+file:///home/jade/lix/nix-eval-jobs",
base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file", authority = std::optional<std::string> = {[contained value] = ""},
path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {["dir"] = "tests/assets"}, fragment = ""}
(rr) up
4 0x00007ffff789d904 in nix::parseFlakeRefWithFragment (url=".#hydraJobs", baseDir=std::optional<std::string> = {...},
allowMissing=allowMissing@entry=false, isFlake=isFlake@entry=true) at src/libexpr/flake/flakeref.cc:179
warning: Source file is more recent than executable.
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
(rr) p parsedURL
$2 = {url = "git+file:///home/jade/lix/nix-eval-jobs", base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file",
authority = std::optional<std::string> = {[contained value] = ""}, path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {
["dir"] = "tests/assets"}, fragment = ""}
(rr) list
174
175 if (pathExists(flakeRoot + "/.git/shallow"))
176 parsedURL.query.insert_or_assign("shallow", "1");
177
178 return std::make_pair(
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
180 fragment);
181 }
```
Change-Id: Ib55a882eaeb3e59228857761dc1e3b2e366b0f5e
2024-06-24 22:49:17 +00:00
|
|
|
input.attrs.insert_or_assign("type", inputType());
|
|
|
|
input.attrs.insert_or_assign("url", url.to_string());
|
|
|
|
return input;
|
2020-03-30 14:04:18 +00:00
|
|
|
}
|
|
|
|
|
2022-12-07 11:58:58 +00:00
|
|
|
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override
|
2020-03-30 14:04:18 +00:00
|
|
|
{
|
2020-10-15 23:35:24 +00:00
|
|
|
auto type = maybeGetStrAttr(attrs, "type");
|
|
|
|
if (type != inputType()) return {};
|
2020-03-30 14:04:18 +00:00
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
// FIXME: some of these only apply to TarballInputScheme.
|
2023-08-22 17:29:25 +00:00
|
|
|
std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack", "rev", "revCount", "lastModified"};
|
2020-03-30 14:04:18 +00:00
|
|
|
for (auto & [name, value] : attrs)
|
2020-10-15 23:35:24 +00:00
|
|
|
if (!allowedNames.count(name))
|
Revert "libfetchers: make attribute / URL query handling consistent"
This reverts commit 35eec921af1043fc6322edc0ad88c872d41623b8.
Reason for revert: Regressed nix-eval-jobs, and it appears to be this change is buggy/missing a case. It just needs another pass.
Code causing the problem in n-e-j, when invoked with `nix-eval-jobs --flake '.#hydraJobs'`:
```
n-e-j/tests/assets » ../../build/src/nix-eval-jobs --meta --workers 1 --flake .#hydraJobs
warning: unknown setting 'trusted-users'
warning: `--gc-roots-dir' not specified
error: unsupported Git input attribute 'dir'
error: worker error: error: unsupported Git input attribute 'dir'
```
```
nix::Value *vRoot = [&]() {
if (args.flake) {
auto [flakeRef, fragment, outputSpec] =
nix::parseFlakeRefWithFragmentAndExtendedOutputsSpec(
args.releaseExpr, nix::absPath("."));
nix::InstallableFlake flake{
{}, state, std::move(flakeRef), fragment, outputSpec,
{}, {}, args.lockFlags};
return flake.toValue(*state).first;
} else {
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
```
Inspecting the program behaviour reveals that `dir` was in fact set in the URL going into the fetcher. This is in turn because unlike in the case changed in this commit, it was not erased before handing it to libfetchers, which is probably just a mistake.
```
(rr) up
3 0x00007ffff60262ae in nix::fetchers::Input::fromURL (url=..., requireTree=requireTree@entry=true) at src/libfetchers/fetchers.cc:39
warning: Source file is more recent than executable.
39 auto res = inputScheme->inputFromURL(url, requireTree);
(rr) p url
$1 = (const nix::ParsedURL &) @0x7fffdc874190: {url = "git+file:///home/jade/lix/nix-eval-jobs",
base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file", authority = std::optional<std::string> = {[contained value] = ""},
path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {["dir"] = "tests/assets"}, fragment = ""}
(rr) up
4 0x00007ffff789d904 in nix::parseFlakeRefWithFragment (url=".#hydraJobs", baseDir=std::optional<std::string> = {...},
allowMissing=allowMissing@entry=false, isFlake=isFlake@entry=true) at src/libexpr/flake/flakeref.cc:179
warning: Source file is more recent than executable.
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
(rr) p parsedURL
$2 = {url = "git+file:///home/jade/lix/nix-eval-jobs", base = "git+file:///home/jade/lix/nix-eval-jobs", scheme = "git+file",
authority = std::optional<std::string> = {[contained value] = ""}, path = "/home/jade/lix/nix-eval-jobs", query = std::map with 1 element = {
["dir"] = "tests/assets"}, fragment = ""}
(rr) list
174
175 if (pathExists(flakeRoot + "/.git/shallow"))
176 parsedURL.query.insert_or_assign("shallow", "1");
177
178 return std::make_pair(
179 FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
180 fragment);
181 }
```
Change-Id: Ib55a882eaeb3e59228857761dc1e3b2e366b0f5e
2024-06-24 22:49:17 +00:00
|
|
|
throw Error("unsupported %s input attribute '%s'", *type, name);
|
2020-03-30 14:04:18 +00:00
|
|
|
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
Input input;
|
|
|
|
input.attrs = attrs;
|
2020-10-15 23:35:24 +00:00
|
|
|
|
2022-02-24 17:09:00 +00:00
|
|
|
//input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
|
2020-03-30 14:04:18 +00:00
|
|
|
return input;
|
|
|
|
}
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
|
2022-12-07 11:58:58 +00:00
|
|
|
ParsedURL toURL(const Input & input) const override
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
{
|
|
|
|
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
2022-12-07 11:58:58 +00:00
|
|
|
// NAR hashes are preferred over file hashes since tar/zip
|
|
|
|
// files don't have a canonical representation.
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
if (auto narHash = input.getNarHash())
|
2020-06-17 08:26:52 +00:00
|
|
|
url.query.insert_or_assign("narHash", narHash->to_string(SRI, true));
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
2022-12-07 11:58:58 +00:00
|
|
|
bool hasAllInfo(const Input & input) const override
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-15 23:35:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FileInputScheme : CurlInputScheme
|
|
|
|
{
|
|
|
|
const std::string inputType() const override { return "file"; }
|
|
|
|
|
2023-08-01 14:07:20 +00:00
|
|
|
bool isValidURL(const ParsedURL & url, bool requireTree) const override
|
2020-10-15 23:35:24 +00:00
|
|
|
{
|
|
|
|
auto parsedUrlScheme = parseUrlScheme(url.scheme);
|
|
|
|
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
|
|
|
|
&& (parsedUrlScheme.application
|
2023-08-01 14:07:20 +00:00
|
|
|
? parsedUrlScheme.application.value() == inputType()
|
|
|
|
: (!requireTree && !hasTarballExtension(url.path)));
|
2020-10-15 23:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"; }
|
|
|
|
|
2023-08-01 14:07:20 +00:00
|
|
|
bool isValidURL(const ParsedURL & url, bool requireTree) const override
|
2020-10-15 23:35:24 +00:00
|
|
|
{
|
|
|
|
auto parsedUrlScheme = parseUrlScheme(url.scheme);
|
|
|
|
|
|
|
|
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
|
|
|
|
&& (parsedUrlScheme.application
|
2023-08-01 14:07:20 +00:00
|
|
|
? parsedUrlScheme.application.value() == inputType()
|
|
|
|
: (requireTree || hasTarballExtension(url.path)));
|
2020-10-15 23:35:24 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
{
|
2023-06-07 12:26:30 +00:00
|
|
|
Input input(_input);
|
|
|
|
auto url = getStrAttr(input.attrs, "url");
|
|
|
|
auto result = downloadTarball(store, url, input.getName(), false);
|
|
|
|
|
|
|
|
if (result.immutableUrl) {
|
|
|
|
auto immutableInput = Input::fromURL(*result.immutableUrl);
|
|
|
|
// FIXME: would be nice to support arbitrary flakerefs
|
|
|
|
// here, e.g. git flakes.
|
|
|
|
if (immutableInput.getType() != "tarball")
|
|
|
|
throw Error("tarball 'Link' headers that redirect to non-tarball URLs are not supported");
|
|
|
|
input = immutableInput;
|
|
|
|
}
|
|
|
|
|
2023-08-22 17:29:25 +00:00
|
|
|
if (result.lastModified && !input.attrs.contains("lastModified"))
|
|
|
|
input.attrs.insert_or_assign("lastModified", uint64_t(result.lastModified));
|
|
|
|
|
2023-06-07 12:26:30 +00:00
|
|
|
return {result.tree.storePath, std::move(input)};
|
Remove TreeInfo
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.
2020-05-29 22:44:11 +00:00
|
|
|
}
|
2020-03-30 14:04:18 +00:00
|
|
|
};
|
|
|
|
|
2020-10-06 11:36:55 +00:00
|
|
|
static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique<TarballInputScheme>()); });
|
2020-10-15 23:35:24 +00:00
|
|
|
static auto rFileInputScheme = OnStartup([] { registerInputScheme(std::make_unique<FileInputScheme>()); });
|
2020-03-30 14:04:18 +00:00
|
|
|
|
|
|
|
}
|