diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 308cff33a..ad61b635a 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -570,8 +570,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()); }); } diff --git a/src/nix/flake.md b/src/nix/flake.md index 2b999431c..67f8037fb 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