From 7c417b47be9c1e4fba7c1cea93722f9c9cb63234 Mon Sep 17 00:00:00 2001 From: Tilman Andre Mix Date: Mon, 9 Sep 2024 20:17:12 +0200 Subject: [PATCH 1/2] feat(libfetchers): add gitea fetcher for flakes adding a gitea type to the flake inputs usable with inputs.input.url = "gitea:repo/owner" the default host is codeberg.org, because it the most popular and most used gitea/forgejo based code forge. host can be changed with ?host=gitea.instance advantage over git+https: git+https://gitea.instance/repo/owner requires git while this new fetcher allows downloading the tarball from gitea. Closes: NixOS/nix#11135 Signed-off-by: Tilman Andre Mix --- src/libfetchers/github.cc | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 2e914164a..e4166e582 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -569,8 +569,83 @@ struct SourceHutInputScheme : GitArchiveInputScheme } }; +struct GiteaInputScheme : GitArchiveInputScheme +{ + std::string_view schemeName() const override { return "gitea"; } + + std::optional> accessHeaderFromToken(const std::string & token) const override + { + // Gitea supports OAuth2 tokens and HTTP Basic + // Authentication. The former simply specifies the token, the + // latter can use the token as the password. Only the first + // is used here. See + // https://docs.gitea.com/development/api-usage#authentication + return std::pair("Authorization", fmt("token %s", token)); + } + + std::string getHost(const Input & input) const + { + return maybeGetStrAttr(input.attrs, "host").value_or("codeberg.org"); + } + + std::string getOwner(const Input & input) const + { + return getStrAttr(input.attrs, "owner"); + } + + std::string getRepo(const Input & input) const + { + return getStrAttr(input.attrs, "repo"); + } + + RefInfo getRevFromRef(nix::ref store, const Input & input) const override + { + auto host = getHost(input); + auto url = fmt("https://%s/api/v1/repos/%s/%s/commits?sha=%s", host, getOwner(input), getRepo(input), *input.getRef()); + + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); + + auto json = nlohmann::json::parse( + readFile( + store->toRealPath( + downloadFile(store, url, "source", headers).storePath))); + + return RefInfo { + .rev = Hash::parseAny(std::string { json[1]["sha"] }, HashAlgorithm::SHA1), + .treeHash = Hash::parseAny(std::string { json[1]["commit"]["tree"]["sha"] }, HashAlgorithm::SHA1) + }; + } + + DownloadUrl getDownloadUrl(const Input & input) const override + { + auto host = getHost(input); + + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); + + // If we have no auth headers then we default to the public archive + // urls so we do not run into rate limits. + const auto urlFmt = headers.empty() ? "https://%s/%s/%s/archive/%s.tar.gz" : "https://%s/api/v1/repos/%s/%s/archive/%s.tar.gz"; + + const auto url = fmt(urlFmt, host, getOwner(input), getRepo(input), + input.getRev()->to_string(HashFormat::Base16, false)); + + return DownloadUrl { url, headers }; + } + + void clone(const Input & input, const Path & destDir) const override + { + auto host = getHost(input); + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", + host, getOwner(input), getRepo(input))) + .applyOverrides(input.getRef(), input.getRev()) + .clone(destDir); + } +}; + + static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rGiteaInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); } From aad7eea00918f62c8bb3d9cc79d6e99f7663e4ac Mon Sep 17 00:00:00 2001 From: Tilman Andre Mix Date: Mon, 9 Sep 2024 20:28:18 +0200 Subject: [PATCH 2/2] doc: add documentation for gitea fetcher Signed-off-by: Tilman Andre Mix --- src/nix/flake.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/nix/flake.md b/src/nix/flake.md index d8ee4ac71..21ddbe468 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -364,6 +364,33 @@ Currently the `type` attribute can be one of the following: * `sourcehut:~misterio/nix-colors/182b4b8709b8ffe4e9774a4c5d6877bf6bb9a21c` * `sourcehut:~misterio/nix-colors/21c1a380a6915d890d408e9f22203436a35bb2de?host=hg.sr.ht` +* `gitea`: Similar to `github`, is a more efficient way to fetch + Gitea/Forgejo repositories. The default host is `codeberg.org`. + The following attributes are required: + + * `owner`: The owner of the repository. + + * `repo`: The name of the repository. + + Like `github`, these are downloaded as tarball archives. + + The URL syntax for `gitea` flakes is: + + `gitea:/(/)?(\?)?` + + `` works the same as `github`. Either a branch or tag name + (`ref`), or a commit hash (`rev`) can be specified. + + Since Gitea/Forgejo allows for self-hosting, you can specify `host` as + a parameter, to point to any instances other than `codeberg.org`. + + Some examples: + + * `gitea:redict/redict` + * `gitea:redict/redict/main` + * `gitea:redict/redict/a4c81102327bc2c74d229784a1d1dd680c708918` + * `gitea:lix-project/lix?host=git.lix.systems` + # Flake format As an example, here is a simple `flake.nix` that depends on the