add sourcehut input scheme

This commit is contained in:
Gabriel Fontes 2021-10-06 13:32:46 -03:00
parent 94992a9196
commit 72e8f94081
No known key found for this signature in database
GPG key ID: 2E54EA7BFE630916
2 changed files with 122 additions and 1 deletions

View file

@ -8,6 +8,7 @@
#include <optional>
#include <nlohmann/json.hpp>
#include <fstream>
namespace nix::fetchers {
@ -17,7 +18,7 @@ struct DownloadUrl
Headers headers;
};
// A github or gitlab host
// A github, gitlab, or sourcehut host
const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);
@ -348,7 +349,95 @@ struct GitLabInputScheme : GitArchiveInputScheme
}
};
struct SourceHutInputScheme : GitArchiveInputScheme
{
std::string type() override { return "sourcehut"; }
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{
// SourceHut supports both PAT and OAuth2. See
// https://man.sr.ht/meta.sr.ht/oauth.md
return std::pair<std::string, std::string>("Authorization", fmt("Bearer %s", token));
// Note: This currently serves no purpose, as this kind of authorization
// does not allow for downloading tarballs on sourcehut private repos.
// Once it is implemented, however, should work as expected.
}
Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
// and with anonymous access, this method should use it instead.
auto ref = *input.getRef();
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto base_url = fmt("https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
Headers headers = makeHeadersWithAuthTokens(host);
std::string ref_uri;
if (ref == "HEAD") {
auto file = store->toRealPath(
downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
std::ifstream is(file);
std::string line;
getline(is, line);
auto ref_index = line.find("ref: ");
if (ref_index == std::string::npos) {
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
}
ref_uri = line.substr(ref_index+5, line.length()-1);
} else
ref_uri = fmt("refs/heads/%s", ref);
auto file = store->toRealPath(
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
std::ifstream is(file);
std::string line;
std::string id;
while(getline(is, line)) {
auto index = line.find(ref_uri);
if (index != std::string::npos) {
id = line.substr(0, index-1);
break;
}
}
if(id.empty())
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
auto rev = Hash::parseAny(id, htSHA1);
debug("HEAD revision for '%s' is %s", fmt("%s/%s", base_url, ref), rev.gitRev());
return rev;
}
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto url = fmt("https://%s/%s/%s/archive/%s.tar.gz",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(Base16, false));
Headers headers = makeHeadersWithAuthTokens(host);
return DownloadUrl { url, headers };
}
void clone(const Input & input, const Path & destDir) override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(fmt("git+https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(destDir);
}
};
static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });
static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique<SourceHutInputScheme>()); });
}

View file

@ -209,6 +209,38 @@ Currently the `type` attribute can be one of the following:
* `github:edolstra/dwarffs/unstable`
* `github:edolstra/dwarffs/d3f2baba8f425779026c6ec04021b2e927f61e31`
* `sourcehut`: Similar to `github`, is a more efficient way to fetch
SourceHut repositories. The following attributes are required:
* `owner`: The owner of the repository (including leading `~`).
* `repo`: The name of the repository.
Like `github`, these are downloaded as tarball archives.
The URL syntax for `sourcehut` flakes is:
`sourcehut:<owner>/<repo>(/<rev-or-ref>)?(\?<params>)?`
`<rev-or-ref>` works the same as `github`. Either a branch or tag name
(`ref`), or a commit hash (`rev`) can be specified.
Since SourceHut allows for self-hosting, you can specify `host` as
a parameter, to point to any instances other than `git.sr.ht`.
Currently, `ref` name resolution only works for Git repositories.
You can refer to Mercurial repositories by simply changing `host` to
`hg.sr.ht` (or any other Mercurial instance). With the caveat
that you must explicitly specify a commit hash (`rev`).
Some examples:
* `sourcehut:~misterio/nix-colors`
* `sourcehut:~misterio/nix-colors/main`
* `sourcehut:~misterio/nix-colors?host=git.example.org`
* `sourcehut:~misterio/nix-colors/182b4b8709b8ffe4e9774a4c5d6877bf6bb9a21c`
* `sourcehut:~misterio/nix-colors/21c1a380a6915d890d408e9f22203436a35bb2de?host=hg.sr.ht`
* `indirect`: Indirections through the flake registry. These have the
form