Files
marketplace/cli/vendor/code.gitea.io/sdk/gitea/miscellaneous.go
T
naudachu 1239fdee70 refactor: move the transport onto the official Gitea SDK
The transport was hand-rolled net/http against the REST API. The payload shapes
were ours, in internal/wire, which meant every field Gitea learned was a field
somebody here had to notice; and "does this instance have issue dependencies?"
had to be guessed from a status code, because a 404 from a missing route and a
404 from a missing issue look alike.

The SDK settles both. The shapes are maintained by the people who maintain the
server, and the client negotiates the server's version when it is built, so the
dependency endpoint is now gated on `>= 1.20.0` — verified against the release
where the route appears, not assumed. Below the gate nothing is requested at all.

internal/wire keeps what the SDK has no answer for: addressing. The SDK takes an
owner, a name and an int64 and never parses, while `42`, `#42`, `owner/repo#42`
and an issue URL are four spellings of one address, all four are what somebody
has in hand, and Key is what the ledger is keyed by. The payload structs go.

Four things that had to survive the move, and did:

- request bodies still land in .kettle/payload/, now via an http.RoundTripper on
  the client the SDK is given — which is better than before, because it files
  every request rather than the ones a call site remembered to name;
- errors still carry the HTTP status AND the response body, and a decode failure
  on a 2xx is deliberately not an APIError, so the dependency probe cannot read
  a bad decode as "feature missing";
- the number -> slug ledger is untouched, entries still outlive the files they
  name;
- Client.For(repo) still re-points at another repository for one call.

What it cost, written down in AGENTS.md where it happened. internal/mapping's
layering test was a fact about the import graph — nothing in its closure could
open a socket — and the SDK ships its types and its client in one package, so
the test now asserts what is still true: the bridge performs no I/O, checked on
direct imports plus a grep for time.Now. A run makes one extra request before it
does anything. Gitea's issue edit endpoint carries no labels, so a push whose
labels changed needs a second call; push makes it and says so. go.mod requires
go 1.26, which the SDK sets and which is now the floor for building this binary.

internal/issue and internal/project are byte-identical. The domain did not
notice, which is the whole argument for the layering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 19:29:38 +05:00

216 lines
6.7 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
)
// GitignoreTemplateInfo represents a gitignore template
type GitignoreTemplateInfo struct {
Name string `json:"name"`
Source string `json:"source"`
}
// LabelTemplate represents a label template
type LabelTemplate struct {
Name string `json:"name"`
Color string `json:"color"`
Description string `json:"description"`
Exclusive bool `json:"exclusive"`
}
// LicensesTemplateListEntry represents a license in the list
type LicensesTemplateListEntry struct {
Key string `json:"key"`
Name string `json:"name"`
URL string `json:"url"`
}
// LicenseTemplateInfo represents a license template
type LicenseTemplateInfo struct {
Key string `json:"key"`
Name string `json:"name"`
URL string `json:"url"`
Body string `json:"body"`
Implementation string `json:"implementation"`
}
// MarkdownOption represents options for rendering markdown
type MarkdownOption struct {
Text string `json:"Text"`
Mode string `json:"Mode"`
Context string `json:"Context"`
Wiki bool `json:"Wiki"`
}
// MarkupOption represents options for rendering markup
type MarkupOption struct {
Text string `json:"Text"`
Mode string `json:"Mode"`
Context string `json:"Context"`
FilePath string `json:"FilePath"`
Wiki bool `json:"Wiki"`
}
// NodeInfo represents nodeinfo about the server
type NodeInfo struct {
Version string `json:"version"`
Software NodeInfoSoftware `json:"software"`
Protocols []string `json:"protocols"`
Services NodeInfoServices `json:"services"`
OpenRegistrations bool `json:"openRegistrations"`
Usage NodeInfoUsage `json:"usage"`
Metadata map[string]interface{} `json:"metadata"`
}
// NodeInfoSoftware represents software information
type NodeInfoSoftware struct {
Name string `json:"name"`
Version string `json:"version"`
Repository string `json:"repository"`
Homepage string `json:"homepage"`
}
// NodeInfoServices represents third party services
type NodeInfoServices struct {
Inbound []string `json:"inbound"`
Outbound []string `json:"outbound"`
}
// NodeInfoUsage represents usage statistics
type NodeInfoUsage struct {
Users NodeInfoUsageUsers `json:"users"`
LocalPosts int64 `json:"localPosts"`
LocalComments int64 `json:"localComments"`
}
// NodeInfoUsageUsers represents user statistics
type NodeInfoUsageUsers struct {
Total int64 `json:"total"`
ActiveHalfyear int64 `json:"activeHalfyear"`
ActiveMonth int64 `json:"activeMonth"`
}
// ListGitignoresTemplates lists all gitignore templates
func (c *Client) ListGitignoresTemplates() ([]string, *Response, error) {
templates := make([]string, 0, 10)
resp, err := c.getParsedResponse("GET", "/gitignore/templates", jsonHeader, nil, &templates)
return templates, resp, err
}
// GetGitignoreTemplateInfo gets information about a gitignore template
func (c *Client) GetGitignoreTemplateInfo(name string) (*GitignoreTemplateInfo, *Response, error) {
if err := escapeValidatePathSegments(&name); err != nil {
return nil, nil, err
}
template := new(GitignoreTemplateInfo)
resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/gitignore/templates/%s", name),
jsonHeader, nil, &template)
return template, resp, err
}
// ListLabelTemplates lists all label templates
func (c *Client) ListLabelTemplates() ([]string, *Response, error) {
templates := make([]string, 0, 10)
resp, err := c.getParsedResponse("GET", "/label/templates", jsonHeader, nil, &templates)
return templates, resp, err
}
// GetLabelTemplate gets all labels in a template
func (c *Client) GetLabelTemplate(name string) ([]*LabelTemplate, *Response, error) {
if err := escapeValidatePathSegments(&name); err != nil {
return nil, nil, err
}
labels := make([]*LabelTemplate, 0, 10)
resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/label/templates/%s", name),
jsonHeader, nil, &labels)
return labels, resp, err
}
// ListLicenseTemplates lists all license templates
func (c *Client) ListLicenseTemplates() ([]*LicensesTemplateListEntry, *Response, error) {
licenses := make([]*LicensesTemplateListEntry, 0, 10)
resp, err := c.getParsedResponse("GET", "/licenses", jsonHeader, nil, &licenses)
return licenses, resp, err
}
// GetLicenseTemplateInfo gets information about a license template
func (c *Client) GetLicenseTemplateInfo(name string) (*LicenseTemplateInfo, *Response, error) {
if err := escapeValidatePathSegments(&name); err != nil {
return nil, nil, err
}
license := new(LicenseTemplateInfo)
resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/licenses/%s", name),
jsonHeader, nil, &license)
return license, resp, err
}
// RenderMarkdown renders a markdown document as HTML
func (c *Client) RenderMarkdown(opt MarkdownOption) (string, *Response, error) {
body, err := json.Marshal(&opt)
if err != nil {
return "", nil, err
}
html, resp, err := c.getResponse("POST", "/markdown", jsonHeader, bytes.NewReader(body))
return string(html), resp, err
}
// RenderMarkdownRaw renders raw markdown as HTML
func (c *Client) RenderMarkdownRaw(markdown string) (string, *Response, error) {
html, resp, err := c.getResponse("POST", "/markdown/raw",
map[string][]string{"Content-Type": {"text/plain"}},
bytes.NewReader([]byte(markdown)))
if err != nil {
return "", resp, err
}
return string(html), resp, err
}
// RenderMarkup renders a markup document as HTML
func (c *Client) RenderMarkup(opt MarkupOption) (string, *Response, error) {
body, err := json.Marshal(&opt)
if err != nil {
return "", nil, err
}
html, resp, err := c.getResponse("POST", "/markup", jsonHeader, bytes.NewReader(body))
if err != nil {
return "", resp, err
}
return string(html), resp, err
}
// GetNodeInfo gets the nodeinfo of the Gitea application
func (c *Client) GetNodeInfo() (*NodeInfo, *Response, error) {
nodeInfo := new(NodeInfo)
resp, err := c.getParsedResponse("GET", "/nodeinfo", jsonHeader, nil, &nodeInfo)
return nodeInfo, resp, err
}
// GetSigningKeyGPG gets the default GPG signing key
func (c *Client) GetSigningKeyGPG() (string, *Response, error) {
key, resp, err := c.getResponse("GET", "/signing-key.gpg", nil, nil)
if err != nil {
return "", resp, err
}
return string(key), resp, err
}
// GetSigningKeySSH gets the default SSH signing key
func (c *Client) GetSigningKeySSH() (string, *Response, error) {
key, resp, err := c.getResponse("GET", "/signing-key.pub", nil, nil)
if err != nil {
return "", resp, err
}
return string(key), resp, err
}