issue_get: GET /repos/{owner}/{repo}/issues/{number} — full issue with labels, assignees, comment count
release_create: POST /repos/{owner}/{repo}/releases — create release and tag in one call
repo_delete: DELETE /repos/{owner}/{repo} — confirm=<repo name> required, blocks accidents
256 lines
6.4 KiB
Go
256 lines
6.4 KiB
Go
package gitea
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
)
|
|
|
|
type Repo struct {
|
|
Name string `json:"name"`
|
|
FullName string `json:"full_name"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
Description string `json:"description"`
|
|
Private bool `json:"private"`
|
|
CloneURL string `json:"clone_url"`
|
|
HTMLURL string `json:"html_url"`
|
|
Template bool `json:"template"`
|
|
}
|
|
|
|
type TreeEntry struct {
|
|
Path string `json:"path"`
|
|
Type string `json:"type"` // "blob" or "tree"
|
|
SHA string `json:"sha"`
|
|
Size int64 `json:"size"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
type Tree struct {
|
|
SHA string `json:"sha"`
|
|
URL string `json:"url"`
|
|
Tree []TreeEntry `json:"tree"`
|
|
Truncated bool `json:"truncated"`
|
|
}
|
|
|
|
func (c *Client) GetTree(ctx context.Context, owner, repo, ref string, recursive bool) (*Tree, error) {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/git/trees/%s", owner, repo, url.PathEscape(ref))
|
|
if recursive {
|
|
path += "?recursive=1"
|
|
}
|
|
body, status, err := c.GetJSON(ctx, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, body); err != nil {
|
|
return nil, err
|
|
}
|
|
var t Tree
|
|
if err := json.Unmarshal(body, &t); err != nil {
|
|
return nil, err
|
|
}
|
|
return &t, nil
|
|
}
|
|
|
|
type Release struct {
|
|
ID int64 `json:"id"`
|
|
TagName string `json:"tag_name"`
|
|
Name string `json:"name"`
|
|
Body string `json:"body"`
|
|
Draft bool `json:"draft"`
|
|
Prerelease bool `json:"prerelease"`
|
|
HTMLURL string `json:"html_url"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
type CreateReleaseArgs struct {
|
|
TagName string `json:"tag_name"`
|
|
Name string `json:"name,omitempty"`
|
|
Body string `json:"body,omitempty"`
|
|
Draft bool `json:"draft,omitempty"`
|
|
Prerelease bool `json:"prerelease,omitempty"`
|
|
// Target branch or commit SHA for tag creation. Empty = repo default branch.
|
|
Target string `json:"target_commitish,omitempty"`
|
|
}
|
|
|
|
func (c *Client) CreateRelease(ctx context.Context, owner, repo string, args CreateReleaseArgs) (*Release, error) {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner, repo)
|
|
body, err := json.Marshal(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, status, err := c.PostJSON(ctx, path, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, resp); err != nil {
|
|
return nil, err
|
|
}
|
|
var r Release
|
|
if err := json.Unmarshal(resp, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
return &r, nil
|
|
}
|
|
|
|
func (c *Client) DeleteRepo(ctx context.Context, owner, repo string) error {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s", owner, repo)
|
|
resp, status, err := c.DeleteJSON(ctx, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if status == 204 {
|
|
return nil
|
|
}
|
|
return MapStatus(status, resp)
|
|
}
|
|
|
|
func (c *Client) UpdateTopics(ctx context.Context, owner, repo string, topics []string) error {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/topics", owner, repo)
|
|
body, err := json.Marshal(map[string][]string{"topics": topics})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, status, err := c.PutJSON(ctx, path, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if status == 204 {
|
|
return nil
|
|
}
|
|
return MapStatus(status, resp)
|
|
}
|
|
|
|
func (c *Client) ListRepos(ctx context.Context, owner string, page, limit int) ([]Repo, error) {
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if limit < 1 {
|
|
limit = 30
|
|
}
|
|
path := fmt.Sprintf("/api/v1/users/%s/repos?page=%d&limit=%d", owner, page, limit)
|
|
body, status, err := c.GetJSON(ctx, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, body); err != nil {
|
|
return nil, err
|
|
}
|
|
var repos []Repo
|
|
if err := json.Unmarshal(body, &repos); err != nil {
|
|
return nil, err
|
|
}
|
|
return repos, nil
|
|
}
|
|
|
|
type repoSearchEnvelope struct {
|
|
Data []Repo `json:"data"`
|
|
OK bool `json:"ok"`
|
|
}
|
|
|
|
func (c *Client) SearchRepos(ctx context.Context, q, owner string, page, limit int) ([]Repo, error) {
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if limit < 1 {
|
|
limit = 30
|
|
}
|
|
path := fmt.Sprintf("/api/v1/repos/search?q=%s&page=%d&limit=%d",
|
|
url.QueryEscape(q), page, limit)
|
|
if owner != "" {
|
|
path += "&owner=" + url.QueryEscape(owner)
|
|
}
|
|
body, status, err := c.GetJSON(ctx, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, body); err != nil {
|
|
return nil, err
|
|
}
|
|
var env repoSearchEnvelope
|
|
if err := json.Unmarshal(body, &env); err != nil {
|
|
return nil, err
|
|
}
|
|
return env.Data, nil
|
|
}
|
|
|
|
type CreateRepoArgs struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Private bool `json:"private,omitempty"`
|
|
AutoInit bool `json:"auto_init,omitempty"`
|
|
DefaultBranch string `json:"default_branch,omitempty"`
|
|
// Org, when non-empty, creates the repo under the named organisation.
|
|
// Uses POST /api/v1/orgs/{org}/repos instead of /api/v1/user/repos.
|
|
Org string `json:"-"`
|
|
}
|
|
|
|
func (c *Client) CreateRepo(ctx context.Context, args CreateRepoArgs) (*Repo, error) {
|
|
var path string
|
|
if args.Org != "" {
|
|
path = fmt.Sprintf("/api/v1/orgs/%s/repos", args.Org)
|
|
} else {
|
|
path = "/api/v1/user/repos"
|
|
}
|
|
body, err := json.Marshal(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, status, err := c.PostJSON(ctx, path, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, resp); err != nil {
|
|
return nil, err
|
|
}
|
|
var r Repo
|
|
if err := json.Unmarshal(resp, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
return &r, nil
|
|
}
|
|
|
|
// UpdateRepoArgs uses pointers so omitempty can distinguish "not set" from false/zero.
|
|
type UpdateRepoArgs struct {
|
|
Description *string `json:"description,omitempty"`
|
|
Private *bool `json:"private,omitempty"`
|
|
Website *string `json:"website,omitempty"`
|
|
DefaultBranch *string `json:"default_branch,omitempty"`
|
|
}
|
|
|
|
func (c *Client) UpdateRepo(ctx context.Context, owner, name string, args UpdateRepoArgs) (*Repo, error) {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s", owner, name)
|
|
body, err := json.Marshal(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, status, err := c.PatchJSON(ctx, path, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, resp); err != nil {
|
|
return nil, err
|
|
}
|
|
var r Repo
|
|
if err := json.Unmarshal(resp, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
return &r, nil
|
|
}
|
|
|
|
func (c *Client) GetRepo(ctx context.Context, owner, name string) (*Repo, error) {
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s", owner, name)
|
|
body, status, err := c.GetJSON(ctx, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := MapStatus(status, body); err != nil {
|
|
return nil, err
|
|
}
|
|
var r Repo
|
|
if err := json.Unmarshal(body, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
return &r, nil
|
|
}
|