package gitea import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "time" ) var ErrTokenNotConfigured = errors.New("gitea API token is not configured") type Repository struct { ID int64 `json:"id"` Owner Owner `json:"owner"` Name string `json:"name"` FullName string `json:"full_name"` CloneURL string `json:"clone_url"` DefaultBranch string `json:"default_branch"` } type Owner struct { Login string `json:"login"` Username string `json:"username"` } func (r Repository) OwnerName() string { if r.Owner.Login != "" { return r.Owner.Login } return r.Owner.Username } type Client struct { baseURL *url.URL token string http *http.Client } func NewClient(baseURL, token string) (*Client, error) { u, err := url.Parse(strings.TrimRight(baseURL, "/")) if err != nil || u.Scheme == "" || u.Host == "" { return nil, fmt.Errorf("invalid GITEA_BASE_URL %q", baseURL) } return &Client{baseURL: u, token: token, http: &http.Client{Timeout: 10 * time.Second}}, nil } func (c *Client) Repository(ctx context.Context, owner, name string) (Repository, error) { if c.token == "" { return Repository{}, ErrTokenNotConfigured } u := c.baseURL.JoinPath("api", "v1", "repos", owner, name) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return Repository{}, err } req.Header.Set("Authorization", "token "+c.token) req.Header.Set("Accept", "application/json") resp, err := c.http.Do(req) if err != nil { return Repository{}, fmt.Errorf("request repository: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return Repository{}, fmt.Errorf("gitea repository lookup returned %s", resp.Status) } var repo Repository if err := json.NewDecoder(resp.Body).Decode(&repo); err != nil { return Repository{}, fmt.Errorf("decode repository: %w", err) } if repo.ID <= 0 || repo.OwnerName() == "" || repo.Name == "" { return Repository{}, errors.New("gitea returned an incomplete repository") } return repo, nil }