Allow gitea dev version (#914)

* update gitea sdk to latest
* As before try to autodetect gitea version, if this does not work, assume it's latest version (v1.17.0 atm)
This commit is contained in:
6543 2022-05-14 17:34:40 +02:00 committed by GitHub
parent c350af645b
commit d06dfc86b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 358 additions and 163 deletions

2
go.mod
View file

@ -3,7 +3,7 @@ module github.com/woodpecker-ci/woodpecker
go 1.16 go 1.16
require ( require (
code.gitea.io/sdk/gitea v0.15.0 code.gitea.io/sdk/gitea v0.15.1-0.20220501190934-319a978c6c71
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.0.2 github.com/bmatcuk/doublestar/v4 v4.0.2

6
go.sum
View file

@ -53,9 +53,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.15.1-0.20220501190934-319a978c6c71 h1:+ZqwhfAftVOAd7AcLpfh4LBdTeJIyt60vGU39zhQPyA=
code.gitea.io/sdk/gitea v0.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg= code.gitea.io/sdk/gitea v0.15.1-0.20220501190934-319a978c6c71/go.mod h1:MuMGvUxT8BmFHa0gHhHsrnz91QfmziXuFffm9AuhMCo=
code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
@ -1866,7 +1865,6 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=

View file

@ -26,8 +26,10 @@ import (
"net/url" "net/url"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/rs/zerolog/log"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
@ -40,6 +42,7 @@ const (
authorizeTokenURL = "%s/login/oauth/authorize" authorizeTokenURL = "%s/login/oauth/authorize"
accessTokenURL = "%s/login/oauth/access_token" accessTokenURL = "%s/login/oauth/access_token"
perPage = 50 perPage = 50
giteaDevVersion = "v1.17.0"
) )
type Gitea struct { type Gitea struct {
@ -458,7 +461,13 @@ func (c *Gitea) newClientToken(ctx context.Context, token string) (*gitea.Client
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
} }
} }
return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx)) client, err := gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx))
if err != nil && strings.Contains(err.Error(), "Malformed version") {
// we guess it's a dev gitea version
log.Error().Err(err).Msgf("could not detect gitea version, assume dev version %s", giteaDevVersion)
client, err = gitea.NewClient(c.URL, gitea.SetGiteaVersion(giteaDevVersion), gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx))
}
return client, err
} }
// getStatus is a helper function that converts a Woodpecker // getStatus is a helper function that converts a Woodpecker

View file

@ -43,7 +43,7 @@ func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt Li
} }
// GetReleaseAttachment returns the requested attachment // GetReleaseAttachment returns the requested attachment
func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) { func (c *Client) GetReleaseAttachment(user, repo string, release, id int64) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil { if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -88,7 +88,7 @@ type EditAttachmentOptions struct {
} }
// EditReleaseAttachment updates the given attachment with the given options // EditReleaseAttachment updates the given attachment with the given options
func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) { func (c *Client) EditReleaseAttachment(user, repo string, release, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil { if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -102,7 +102,7 @@ func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachm
} }
// DeleteReleaseAttachment deletes the given attachment including the uploaded file // DeleteReleaseAttachment deletes the given attachment including the uploaded file
func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) { func (c *Client) DeleteReleaseAttachment(user, repo string, release, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil { if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err return nil, err
} }

View file

@ -6,9 +6,9 @@
package gitea package gitea
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -24,23 +24,25 @@ var jsonHeader = http.Header{"content-type": []string{"application/json"}}
// Version return the library version // Version return the library version
func Version() string { func Version() string {
return "0.14.0" return "0.16.0"
} }
// Client represents a thread-safe Gitea API client. // Client represents a thread-safe Gitea API client.
type Client struct { type Client struct {
url string url string
accessToken string accessToken string
username string username string
password string password string
otp string otp string
sudo string sudo string
debug bool debug bool
client *http.Client client *http.Client
ctx context.Context ctx context.Context
mutex sync.RWMutex mutex sync.RWMutex
serverVersion *version.Version serverVersion *version.Version
getVersionOnce sync.Once getVersionOnce sync.Once
ignoreVersion bool // only set by SetGiteaVersion so don't need a mutex lock
} }
// Response represents the gitea response // Response represents the gitea response
@ -48,16 +50,21 @@ type Response struct {
*http.Response *http.Response
} }
// ClientOption are functions used to init a new client
type ClientOption func(*Client) error
// NewClient initializes and returns a API client. // NewClient initializes and returns a API client.
// Usage of all gitea.Client methods is concurrency-safe. // Usage of all gitea.Client methods is concurrency-safe.
func NewClient(url string, options ...func(*Client)) (*Client, error) { func NewClient(url string, options ...ClientOption) (*Client, error) {
client := &Client{ client := &Client{
url: strings.TrimSuffix(url, "/"), url: strings.TrimSuffix(url, "/"),
client: &http.Client{}, client: &http.Client{},
ctx: context.Background(), ctx: context.Background(),
} }
for _, opt := range options { for _, opt := range options {
opt(client) if err := opt(client); err != nil {
return nil, err
}
} }
if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
return nil, err return nil, err
@ -73,9 +80,10 @@ func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
} }
// SetHTTPClient is an option for NewClient to set custom http client // SetHTTPClient is an option for NewClient to set custom http client
func SetHTTPClient(httpClient *http.Client) func(client *Client) { func SetHTTPClient(httpClient *http.Client) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.SetHTTPClient(httpClient) client.SetHTTPClient(httpClient)
return nil
} }
} }
@ -87,18 +95,20 @@ func (c *Client) SetHTTPClient(client *http.Client) {
} }
// SetToken is an option for NewClient to set token // SetToken is an option for NewClient to set token
func SetToken(token string) func(client *Client) { func SetToken(token string) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.mutex.Lock() client.mutex.Lock()
client.accessToken = token client.accessToken = token
client.mutex.Unlock() client.mutex.Unlock()
return nil
} }
} }
// SetBasicAuth is an option for NewClient to set username and password // SetBasicAuth is an option for NewClient to set username and password
func SetBasicAuth(username, password string) func(client *Client) { func SetBasicAuth(username, password string) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.SetBasicAuth(username, password) client.SetBasicAuth(username, password)
return nil
} }
} }
@ -110,9 +120,10 @@ func (c *Client) SetBasicAuth(username, password string) {
} }
// SetOTP is an option for NewClient to set OTP for 2FA // SetOTP is an option for NewClient to set OTP for 2FA
func SetOTP(otp string) func(client *Client) { func SetOTP(otp string) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.SetOTP(otp) client.SetOTP(otp)
return nil
} }
} }
@ -123,14 +134,15 @@ func (c *Client) SetOTP(otp string) {
c.mutex.Unlock() c.mutex.Unlock()
} }
// SetContext is an option for NewClient to set context // SetContext is an option for NewClient to set the default context
func SetContext(ctx context.Context) func(client *Client) { func SetContext(ctx context.Context) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.SetContext(ctx) client.SetContext(ctx)
return nil
} }
} }
// SetContext set context witch is used for http requests // SetContext set default context witch is used for http requests
func (c *Client) SetContext(ctx context.Context) { func (c *Client) SetContext(ctx context.Context) {
c.mutex.Lock() c.mutex.Lock()
c.ctx = ctx c.ctx = ctx
@ -138,9 +150,10 @@ func (c *Client) SetContext(ctx context.Context) {
} }
// SetSudo is an option for NewClient to set sudo header // SetSudo is an option for NewClient to set sudo header
func SetSudo(sudo string) func(client *Client) { func SetSudo(sudo string) ClientOption {
return func(client *Client) { return func(client *Client) error {
client.SetSudo(sudo) client.SetSudo(sudo)
return nil
} }
} }
@ -152,11 +165,12 @@ func (c *Client) SetSudo(sudo string) {
} }
// SetDebugMode is an option for NewClient to enable debug mode // SetDebugMode is an option for NewClient to enable debug mode
func SetDebugMode() func(client *Client) { func SetDebugMode() ClientOption {
return func(client *Client) { return func(client *Client) error {
client.mutex.Lock() client.mutex.Lock()
client.debug = true client.debug = true
client.mutex.Unlock() client.mutex.Unlock()
return nil
} }
} }
@ -185,14 +199,20 @@ func (c *Client) getWebResponse(method, path string, body io.Reader) ([]byte, *R
if debug { if debug {
fmt.Printf("Response: %v\n\n", resp) fmt.Printf("Response: %v\n\n", resp)
} }
return data, &Response{resp}, nil return data, &Response{resp}, err
} }
func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) { func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) {
c.mutex.RLock() c.mutex.RLock()
debug := c.debug debug := c.debug
if debug { if debug {
fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, body) var bodyStr string
if body != nil {
bs, _ := ioutil.ReadAll(body)
body = bytes.NewReader(bs)
bodyStr = string(bs)
}
fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, bodyStr)
} }
req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body) req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body)
if err != nil { if err != nil {
@ -248,27 +268,23 @@ func statusCodeToErr(resp *Response) (body []byte, err error) {
return nil, fmt.Errorf("body read on HTTP error %d: %v", resp.StatusCode, err) return nil, fmt.Errorf("body read on HTTP error %d: %v", resp.StatusCode, err)
} }
switch resp.StatusCode { // Try to unmarshal and get an error message
case 403:
return data, errors.New("403 Forbidden")
case 404:
return data, errors.New("404 Not Found")
case 409:
return data, errors.New("409 Conflict")
case 422:
return data, fmt.Errorf("422 Unprocessable Entity: %s", string(data))
}
path := resp.Request.URL.Path
method := resp.Request.Method
header := resp.Request.Header
errMap := make(map[string]interface{}) errMap := make(map[string]interface{})
if err = json.Unmarshal(data, &errMap); err != nil { if err = json.Unmarshal(data, &errMap); err != nil {
// when the JSON can't be parsed, data was probably empty or a // when the JSON can't be parsed, data was probably empty or a
// plain string, so we try to return a helpful error anyway // plain string, so we try to return a helpful error anyway
path := resp.Request.URL.Path
method := resp.Request.Method
header := resp.Request.Header
return data, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data)) return data, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
} }
return data, errors.New(errMap["message"].(string))
if msg, ok := errMap["message"]; ok {
return data, fmt.Errorf("%v", msg)
}
// If no error message, at least give status and data
return data, fmt.Errorf("%s: %s", resp.Status, string(data))
} }
func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) { func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) {

View file

@ -16,7 +16,7 @@ type ListForksOptions struct {
} }
// ListForks list a repository's forks // ListForks list a repository's forks
func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) { func (c *Client) ListForks(user, repo string, opt ListForksOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil { if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -32,6 +32,8 @@ func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*R
type CreateForkOption struct { type CreateForkOption struct {
// organization name, if forking into an organization // organization name, if forking into an organization
Organization *string `json:"organization"` Organization *string `json:"organization"`
// name of the forked repository
Name *string `json:"name"`
} }
// CreateFork create a fork of a repository // CreateFork create a fork of a repository

View file

@ -3,7 +3,6 @@ module code.gitea.io/sdk/gitea
go 1.13 go 1.13
require ( require (
code.gitea.io/gitea-vet v0.2.1 // indirect github.com/hashicorp/go-version v1.4.0
github.com/hashicorp/go-version v1.2.1 github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.4.0
) )

View file

@ -1,33 +1,13 @@
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

59
vendor/code.gitea.io/sdk/gitea/hook_validate.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2022 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"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"net/http"
)
// VerifyWebhookSignature verifies that a payload matches the X-Gitea-Signature based on a secret
func VerifyWebhookSignature(secret, expected string, payload []byte) (bool, error) {
hash := hmac.New(sha256.New, []byte(secret))
if _, err := hash.Write(payload); err != nil {
return false, err
}
expectedSum, err := hex.DecodeString(expected)
if err != nil {
return false, err
}
return hmac.Equal(hash.Sum(nil), expectedSum), nil
}
// VerifyWebhookSignatureMiddleware is a http.Handler for verifying X-Gitea-Signature on incoming webhooks
func VerifyWebhookSignatureMiddleware(secret string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var b bytes.Buffer
if _, err := io.Copy(&b, r.Body); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
expected := r.Header.Get("X-Gitea-Signature")
if expected == "" {
http.Error(w, "no signature found", http.StatusBadRequest)
return
}
ok, err := VerifyWebhookSignature(secret, expected, b.Bytes())
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
if !ok {
http.Error(w, "invalid payload", http.StatusUnauthorized)
return
}
r.Body = io.NopCloser(&b)
next.ServeHTTP(w, r)
})
}
}

View file

@ -71,6 +71,10 @@ type ListIssueOption struct {
AssignedBy string AssignedBy string
// filter by username mentioned // filter by username mentioned
MentionedBy string MentionedBy string
// filter by owner (only works on ListIssues on User)
Owner string
// filter by team (requires organization owner parameter to be provided and only works on ListIssues on User)
Team string
} }
// StateType issue state type // StateType issue state type
@ -135,6 +139,12 @@ func (opt *ListIssueOption) QueryEncode() string {
if len(opt.MentionedBy) > 0 { if len(opt.MentionedBy) > 0 {
query.Add("mentioned_by", opt.MentionedBy) query.Add("mentioned_by", opt.MentionedBy)
} }
if len(opt.Owner) > 0 {
query.Add("owner", opt.Owner)
}
if len(opt.Team) > 0 {
query.Add("team", opt.MentionedBy)
}
return query.Encode() return query.Encode()
} }

View file

@ -72,8 +72,8 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Respon
} }
// GetMilestoneByName get one milestone by repo and milestone name // GetMilestoneByName get one milestone by repo and milestone name
func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone, *Response, error) { func (c *Client) GetMilestoneByName(owner, repo, name string) (*Milestone, *Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode // backwards compatibility mode
m, resp, err := c.resolveMilestoneByName(owner, repo, name) m, resp, err := c.resolveMilestoneByName(owner, repo, name)
return m, resp, err return m, resp, err
@ -163,8 +163,8 @@ func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOp
} }
// EditMilestoneByName modify milestone with options // EditMilestoneByName modify milestone with options
func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMilestoneOption) (*Milestone, *Response, error) { func (c *Client) EditMilestoneByName(owner, repo, name string, opt EditMilestoneOption) (*Milestone, *Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode // backwards compatibility mode
m, _, err := c.resolveMilestoneByName(owner, repo, name) m, _, err := c.resolveMilestoneByName(owner, repo, name)
if err != nil { if err != nil {
@ -197,8 +197,8 @@ func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error
} }
// DeleteMilestoneByName delete one milestone by name // DeleteMilestoneByName delete one milestone by name
func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Response, error) { func (c *Client) DeleteMilestoneByName(owner, repo, name string) (*Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode // backwards compatibility mode
m, _, err := c.resolveMilestoneByName(owner, repo, name) m, _, err := c.resolveMilestoneByName(owner, repo, name)
if err != nil { if err != nil {
@ -229,7 +229,7 @@ func (c *Client) resolveMilestoneByName(owner, repo, name string) (*Milestone, *
return nil, nil, fmt.Errorf("milestone '%s' do not exist", name) return nil, nil, fmt.Errorf("milestone '%s' do not exist", name)
} }
for _, m := range miles { for _, m := range miles {
if strings.ToLower(strings.TrimSpace(m.Title)) == strings.ToLower(strings.TrimSpace(name)) { if strings.EqualFold(strings.TrimSpace(m.Title), strings.TrimSpace(name)) {
return m, resp, nil return m, resp, nil
} }
} }

View file

@ -9,12 +9,13 @@ import (
"net/url" "net/url"
) )
const defaultPageSize = 10
const maxPageSize = 50
// ListOptions options for using Gitea's API pagination // ListOptions options for using Gitea's API pagination
type ListOptions struct { type ListOptions struct {
Page int // Setting Page to -1 disables pagination on endpoints that support it.
// Page numbering starts at 1.
Page int
// The default value depends on the server config DEFAULT_PAGING_NUM
// The highest valid value depends on the server config MAX_RESPONSE_ITEMS
PageSize int PageSize int
} }
@ -26,8 +27,9 @@ func (o ListOptions) getURLQuery() url.Values {
return query return query
} }
// setDefaults set default pagination options if none or wrong are set // setDefaults applies default pagination options.
// if you set -1 as page it will set all to 0 // If .Page is set to -1, it will disable pagination.
// WARNING: This function is not idempotent, make sure to never call this method twice!
func (o *ListOptions) setDefaults() { func (o *ListOptions) setDefaults() {
if o.Page < 0 { if o.Page < 0 {
o.Page, o.PageSize = 0, 0 o.Page, o.PageSize = 0, 0
@ -35,8 +37,4 @@ func (o *ListOptions) setDefaults() {
} else if o.Page == 0 { } else if o.Page == 0 {
o.Page = 1 o.Page = 1
} }
if o.PageSize < 0 || o.PageSize > maxPageSize {
o.PageSize = defaultPageSize
}
} }

View file

@ -8,12 +8,6 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"time" "time"
"github.com/hashicorp/go-version"
)
var (
version1_12_3, _ = version.NewVersion("1.12.3")
) )
// NotificationThread expose Notification on API // NotificationThread expose Notification on API
@ -29,11 +23,13 @@ type NotificationThread struct {
// NotificationSubject contains the notification subject (Issue/Pull/Commit) // NotificationSubject contains the notification subject (Issue/Pull/Commit)
type NotificationSubject struct { type NotificationSubject struct {
Title string `json:"title"` Title string `json:"title"`
URL string `json:"url"` URL string `json:"url"`
LatestCommentURL string `json:"latest_comment_url"` HTMLURL string `json:"html_url"`
Type NotifySubjectType `json:"type"` LatestCommentURL string `json:"latest_comment_url"`
State NotifySubjectState `json:"state"` LatestCommentHTMLURL string `json:"latest_comment_html_url"`
Type NotifySubjectType `json:"type"`
State NotifySubjectState `json:"state"`
} }
// NotifyStatus notification status type // NotifyStatus notification status type

View file

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
) )
// Team represents a team in an organization // Team represents a team in an organization
@ -75,6 +76,44 @@ func (c *Client) GetTeam(id int64) (*Team, *Response, error) {
return t, resp, err return t, resp, err
} }
// SearchTeamsOptions options for searching teams.
type SearchTeamsOptions struct {
ListOptions
Query string
IncludeDescription bool
}
func (o SearchTeamsOptions) getURLQuery() url.Values {
query := make(url.Values)
query.Add("page", fmt.Sprintf("%d", o.Page))
query.Add("limit", fmt.Sprintf("%d", o.PageSize))
query.Add("q", o.Query)
query.Add("include_desc", fmt.Sprintf("%t", o.IncludeDescription))
return query
}
// TeamSearchResults is the JSON struct that is returned from Team search API.
type TeamSearchResults struct {
OK bool `json:"ok"`
Error string `json:"error"`
Data []*Team `json:"data"`
}
// SearchOrgTeams search for teams in a org.
func (c *Client) SearchOrgTeams(org string, opt *SearchTeamsOptions) ([]*Team, *Response, error) {
responseBody := TeamSearchResults{}
opt.setDefaults()
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams/search?%s", org, opt.getURLQuery().Encode()), nil, nil, &responseBody)
if err != nil {
return nil, resp, err
}
if !responseBody.OK {
return nil, resp, fmt.Errorf("gitea error: %v", responseBody.Error)
}
return responseBody.Data, resp, err
}
// CreateTeamOption options for creating a team // CreateTeamOption options for creating a team
type CreateTeamOption struct { type CreateTeamOption struct {
Name string `json:"name"` Name string `json:"name"`
@ -86,7 +125,7 @@ type CreateTeamOption struct {
} }
// Validate the CreateTeamOption struct // Validate the CreateTeamOption struct
func (opt CreateTeamOption) Validate() error { func (opt *CreateTeamOption) Validate() error {
if opt.Permission == AccessModeOwner { if opt.Permission == AccessModeOwner {
opt.Permission = AccessModeAdmin opt.Permission = AccessModeAdmin
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin { } else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
@ -109,7 +148,7 @@ func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response,
if err := escapeValidatePathSegments(&org); err != nil { if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := opt.Validate(); err != nil { if err := (&opt).Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
@ -132,7 +171,7 @@ type EditTeamOption struct {
} }
// Validate the EditTeamOption struct // Validate the EditTeamOption struct
func (opt EditTeamOption) Validate() error { func (opt *EditTeamOption) Validate() error {
if opt.Permission == AccessModeOwner { if opt.Permission == AccessModeOwner {
opt.Permission = AccessModeAdmin opt.Permission = AccessModeAdmin
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin { } else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
@ -152,7 +191,7 @@ func (opt EditTeamOption) Validate() error {
// EditTeam edits a team of an organization // EditTeam edits a team of an organization
func (c *Client) EditTeam(id int64, opt EditTeamOption) (*Response, error) { func (c *Client) EditTeam(id int64, opt EditTeamOption) (*Response, error) {
if err := opt.Validate(); err != nil { if err := (&opt).Validate(); err != nil {
return nil, err return nil, err
} }
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)

View file

@ -209,15 +209,18 @@ func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRe
// MergePullRequestOption options when merging a pull request // MergePullRequestOption options when merging a pull request
type MergePullRequestOption struct { type MergePullRequestOption struct {
Style MergeStyle `json:"Do"` Style MergeStyle `json:"Do"`
Title string `json:"MergeTitleField"` MergeCommitID string `json:"MergeCommitID"`
Message string `json:"MergeMessageField"` Title string `json:"MergeTitleField"`
Message string `json:"MergeMessageField"`
DeleteBranchAfterMerge bool `json:"delete_branch_after_merge"`
ForceMerge bool `json:"force_merge"`
} }
// Validate the MergePullRequestOption struct // Validate the MergePullRequestOption struct
func (opt MergePullRequestOption) Validate(c *Client) error { func (opt MergePullRequestOption) Validate(c *Client) error {
if opt.Style == MergeStyleSquash { if opt.Style == MergeStyleSquash {
if err := c.CheckServerVersionConstraint(">=1.11.5"); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_11_5); err != nil {
return err return err
} }
} }
@ -249,7 +252,6 @@ func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Re
return false, nil, err return false, nil, err
} }
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@ -257,9 +259,29 @@ func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Re
return status == 204, resp, nil return status == 204, resp, nil
} }
// PullRequestDiffOptions options for GET /repos/<owner>/<repo>/pulls/<idx>.[diff|patch]
type PullRequestDiffOptions struct {
// Include binary file changes when requesting a .diff
Binary bool
}
// QueryEncode converts the options to a query string
func (o PullRequestDiffOptions) QueryEncode() string {
query := make(url.Values)
query.Add("binary", fmt.Sprintf("%v", o.Binary))
return query.Encode()
}
type pullRequestDiffType string
const (
pullRequestDiffTypeDiff pullRequestDiffType = "diff"
pullRequestDiffTypePatch pullRequestDiffType = "patch"
)
// getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR // getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR
func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) { func (c *Client) getPullRequestDiffOrPatch(owner, repo string, kind pullRequestDiffType, index int64, opts PullRequestDiffOptions) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &kind); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
@ -270,19 +292,20 @@ func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64
if r.Private { if r.Private {
return nil, nil, err return nil, nil, err
} }
return c.getWebResponse("GET", fmt.Sprintf("/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil) url := fmt.Sprintf("/%s/%s/pulls/%d.%s?%s", owner, repo, index, kind, opts.QueryEncode())
return c.getWebResponse("GET", url, nil)
} }
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil, nil) return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil, nil)
} }
// GetPullRequestPatch gets the .patch file as bytes for a PR // GetPullRequestPatch gets the git patchset of a PR
func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *Response, error) { func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *Response, error) {
return c.getPullRequestDiffOrPatch(owner, repo, "patch", index) return c.getPullRequestDiffOrPatch(owner, repo, pullRequestDiffTypePatch, index, PullRequestDiffOptions{})
} }
// GetPullRequestDiff gets the .diff file as bytes for a PR // GetPullRequestDiff gets the diff of a PR. For Gitea >= 1.16, you must set includeBinary to get an applicable diff
func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) { func (c *Client) GetPullRequestDiff(owner, repo string, index int64, opts PullRequestDiffOptions) ([]byte, *Response, error) {
return c.getPullRequestDiffOrPatch(owner, repo, "diff", index) return c.getPullRequestDiffOrPatch(owner, repo, pullRequestDiffTypeDiff, index, opts)
} }
// ListPullRequestCommitsOptions options for listing pull requests // ListPullRequestCommitsOptions options for listing pull requests

View file

@ -116,7 +116,7 @@ type ListPullReviewsOptions struct {
// Validate the CreatePullReviewOptions struct // Validate the CreatePullReviewOptions struct
func (opt CreatePullReviewOptions) Validate() error { func (opt CreatePullReviewOptions) Validate() error {
if opt.State != ReviewStateApproved && len(strings.TrimSpace(opt.Body)) == 0 { if opt.State != ReviewStateApproved && len(opt.Comments) == 0 && len(strings.TrimSpace(opt.Body)) == 0 {
return fmt.Errorf("body is empty") return fmt.Errorf("body is empty")
} }
for i := range opt.Comments { for i := range opt.Comments {

View file

@ -47,7 +47,7 @@ func (opt *ListReleasesOptions) QueryEncode() string {
query.Add("draft", fmt.Sprintf("%t", *opt.IsDraft)) query.Add("draft", fmt.Sprintf("%t", *opt.IsDraft))
} }
if opt.IsPreRelease != nil { if opt.IsPreRelease != nil {
query.Add("draft", fmt.Sprintf("%t", *opt.IsPreRelease)) query.Add("pre-release", fmt.Sprintf("%t", *opt.IsPreRelease))
} }
return query.Encode() return query.Encode()
@ -79,7 +79,7 @@ func (c *Client) GetRelease(owner, repo string, id int64) (*Release, *Response,
} }
// GetReleaseByTag get a release of a repository by tag // GetReleaseByTag get a release of a repository by tag
func (c *Client) GetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) { func (c *Client) GetReleaseByTag(owner, repo, tag string) (*Release, *Response, error) {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
return c.fallbackGetReleaseByTag(owner, repo, tag) return c.fallbackGetReleaseByTag(owner, repo, tag)
} }
@ -168,7 +168,7 @@ func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
} }
// DeleteReleaseByTag deletes a release frm a repository by tag // DeleteReleaseByTag deletes a release frm a repository by tag
func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, error) { func (c *Client) DeleteReleaseByTag(user, repo, tag string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil { if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, err return nil, err
} }
@ -182,7 +182,7 @@ func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, e
} }
// fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 ) // fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 )
func (c *Client) fallbackGetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) { func (c *Client) fallbackGetReleaseByTag(owner, repo, tag string) (*Release, *Response, error) {
for i := 1; ; i++ { for i := 1; ; i++ {
rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions: ListOptions{Page: i}}) rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions: ListOptions{Page: i}})
if err != nil { if err != nil {

View file

@ -93,6 +93,7 @@ type Repository struct {
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
Internal bool `json:"internal"` Internal bool `json:"internal"`
MirrorInterval string `json:"mirror_interval"` MirrorInterval string `json:"mirror_interval"`
MirrorUpdated time.Time `json:"mirror_updated,omitempty"`
DefaultMergeStyle MergeStyle `json:"default_merge_style"` DefaultMergeStyle MergeStyle `json:"default_merge_style"`
} }
@ -286,7 +287,9 @@ func (c *Client) SearchRepos(opt SearchRepoOptions) ([]*Repository, *Response, e
// private repos only not supported on gitea <= 1.11.x // private repos only not supported on gitea <= 1.11.x
return nil, nil, err return nil, nil, err
} }
link.Query().Add("private", "false") newQuery := link.Query()
newQuery.Add("private", "false")
link.RawQuery = newQuery.Encode()
} }
} }
@ -329,13 +332,13 @@ func (opt CreateRepoOption) Validate(c *Client) error {
return fmt.Errorf("name has more than 100 chars") return fmt.Errorf("name has more than 100 chars")
} }
if len(opt.Description) > 255 { if len(opt.Description) > 255 {
return fmt.Errorf("name has more than 255 chars") return fmt.Errorf("description has more than 255 chars")
} }
if len(opt.DefaultBranch) > 100 { if len(opt.DefaultBranch) > 100 {
return fmt.Errorf("name has more than 100 chars") return fmt.Errorf("default branch name has more than 100 chars")
} }
if len(opt.TrustModel) != 0 { if len(opt.TrustModel) != 0 {
if err := c.CheckServerVersionConstraint(">=1.13.0"); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return err return err
} }
} }

View file

@ -66,7 +66,7 @@ const (
) )
// Validate the AddCollaboratorOption struct // Validate the AddCollaboratorOption struct
func (opt AddCollaboratorOption) Validate() error { func (opt *AddCollaboratorOption) Validate() error {
if opt.Permission != nil { if opt.Permission != nil {
if *opt.Permission == AccessModeOwner { if *opt.Permission == AccessModeOwner {
*opt.Permission = AccessModeAdmin *opt.Permission = AccessModeAdmin
@ -88,7 +88,7 @@ func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollabo
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil { if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return nil, err return nil, err
} }
if err := opt.Validate(); err != nil { if err := (&opt).Validate(); err != nil {
return nil, err return nil, err
} }
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)

View file

@ -32,11 +32,19 @@ type CommitUser struct {
// RepoCommit contains information of a commit in the context of a repository. // RepoCommit contains information of a commit in the context of a repository.
type RepoCommit struct { type RepoCommit struct {
URL string `json:"url"` URL string `json:"url"`
Author *CommitUser `json:"author"` Author *CommitUser `json:"author"`
Committer *CommitUser `json:"committer"` Committer *CommitUser `json:"committer"`
Message string `json:"message"` Message string `json:"message"`
Tree *CommitMeta `json:"tree"` Tree *CommitMeta `json:"tree"`
Verification *PayloadCommitVerification `json:"verification"`
}
// CommitStats contains stats from a Git commit
type CommitStats struct {
Total int `json:"total"`
Additions int `json:"additions"`
Deletions int `json:"deletions"`
} }
// Commit contains information generated from a Git commit. // Commit contains information generated from a Git commit.
@ -48,6 +56,7 @@ type Commit struct {
Committer *User `json:"committer"` Committer *User `json:"committer"`
Parents []*CommitMeta `json:"parents"` Parents []*CommitMeta `json:"parents"`
Files []*CommitAffectedFiles `json:"files"` Files []*CommitAffectedFiles `json:"files"`
Stats *CommitStats `json:"stats"`
} }
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE // CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
@ -74,16 +83,21 @@ func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Respons
// ListCommitOptions list commit options // ListCommitOptions list commit options
type ListCommitOptions struct { type ListCommitOptions struct {
ListOptions ListOptions
//SHA or branch to start listing commits from (usually 'master') // SHA or branch to start listing commits from (usually 'master')
SHA string SHA string
// Path indicates that only commits that include the path's file/dir should be returned.
Path string
} }
// QueryEncode turns options into querystring argument // QueryEncode turns options into querystring argument
func (opt *ListCommitOptions) QueryEncode() string { func (opt *ListCommitOptions) QueryEncode() string {
query := opt.ListOptions.getURLQuery() query := opt.getURLQuery()
if opt.SHA != "" { if opt.SHA != "" {
query.Add("sha", opt.SHA) query.Add("sha", opt.SHA)
} }
if opt.Path != "" {
query.Add("path", opt.Path)
}
return query.Encode() return query.Encode()
} }

View file

@ -16,7 +16,7 @@ type GitServiceType string
const ( const (
// GitServicePlain represents a plain git service // GitServicePlain represents a plain git service
GitServicePlain GitServiceType = "git" GitServicePlain GitServiceType = "git"
//GitServiceGithub represents github.com // GitServiceGithub represents github.com
GitServiceGithub GitServiceType = "github" GitServiceGithub GitServiceType = "github"
// GitServiceGitlab represents a gitlab service // GitServiceGitlab represents a gitlab service
GitServiceGitlab GitServiceType = "gitlab" GitServiceGitlab GitServiceType = "gitlab"

View file

@ -34,3 +34,29 @@ func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer", owner, reponame), jsonHeader, bytes.NewReader(body), repo) resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
return repo, resp, err return repo, resp, err
} }
// AcceptRepoTransfer accepts a repo transfer.
func (c *Client) AcceptRepoTransfer(owner, reponame string) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
return nil, nil, err
}
repo := new(Repository)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer/accept", owner, reponame), jsonHeader, nil, repo)
return repo, resp, err
}
// RejectRepoTransfer rejects a repo transfer.
func (c *Client) RejectRepoTransfer(owner, reponame string) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
return nil, nil, err
}
repo := new(Repository)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer/reject", owner, reponame), jsonHeader, nil, repo)
return repo, resp, err
}

View file

@ -35,7 +35,7 @@ func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeRespo
return nil, nil, err return nil, nil, err
} }
trees := new(GitTreeResponse) trees := new(GitTreeResponse)
var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref) path := fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
if recursive { if recursive {
path += "?recursive=1" path += "?recursive=1"
} }

View file

@ -33,7 +33,7 @@ func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error)
// GetMyWatchedRepos list repositories watched by the authenticated user // GetMyWatchedRepos list repositories watched by the authenticated user
func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) { func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) {
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/subscriptions"), nil, nil, &repos) resp, err := c.getParsedResponse("GET", "/user/subscriptions", nil, nil, &repos)
return repos, resp, err return repos, resp, err
} }

View file

@ -77,7 +77,6 @@ func (c *Client) GetUserByID(id int64) (*User, *Response, error) {
query := make(url.Values) query := make(url.Values)
query.Add("uid", strconv.FormatInt(id, 10)) query.Add("uid", strconv.FormatInt(id, 10))
users, resp, err := c.searchUsers(query.Encode()) users, resp, err := c.searchUsers(query.Encode())
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View file

@ -71,13 +71,13 @@ func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed") return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
} }
var token = "" token := ""
switch reflect.ValueOf(value).Kind() { switch reflect.ValueOf(value).Kind() {
case reflect.Int64: case reflect.Int64:
token = fmt.Sprintf("%d", value.(int64)) token = fmt.Sprintf("%d", value.(int64))
case reflect.String: case reflect.String:
if err := c.CheckServerVersionConstraint(">= 1.13.0"); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, err return nil, err
} }
token = value.(string) token = value.(string)

View file

@ -12,7 +12,7 @@ import (
// ServerVersion returns the version of the server // ServerVersion returns the version of the server
func (c *Client) ServerVersion() (string, *Response, error) { func (c *Client) ServerVersion() (string, *Response, error) {
var v = struct { v := struct {
Version string `json:"version"` Version string `json:"version"`
}{} }{}
resp, err := c.getParsedResponse("GET", "/version", nil, nil, &v) resp, err := c.getParsedResponse("GET", "/version", nil, nil, &v)
@ -39,17 +39,41 @@ func (c *Client) CheckServerVersionConstraint(constraint string) error {
return nil return nil
} }
// SetGiteaVersion configures the Client to assume the given version of the
// Gitea server, instead of querying the server for it when initializing.
// Use "" to skip all canonical ways in the SDK to check for versions
func SetGiteaVersion(v string) ClientOption {
if v == "" {
return func(c *Client) error {
c.ignoreVersion = true
return nil
}
}
return func(c *Client) (err error) {
c.getVersionOnce.Do(func() {
c.serverVersion, err = version.NewVersion(v)
})
return
}
}
// predefined versions only have to be parsed by library once // predefined versions only have to be parsed by library once
var ( var (
version1_11_0, _ = version.NewVersion("1.11.0") version1_11_0 = version.Must(version.NewVersion("1.11.0"))
version1_12_0, _ = version.NewVersion("1.12.0") version1_11_5 = version.Must(version.NewVersion("1.11.5"))
version1_13_0, _ = version.NewVersion("1.13.0") version1_12_0 = version.Must(version.NewVersion("1.12.0"))
version1_14_0, _ = version.NewVersion("1.14.0") version1_12_3 = version.Must(version.NewVersion("1.12.3"))
version1_15_0, _ = version.NewVersion("1.15.0") version1_13_0 = version.Must(version.NewVersion("1.13.0"))
version1_14_0 = version.Must(version.NewVersion("1.14.0"))
version1_15_0 = version.Must(version.NewVersion("1.15.0"))
version1_16_0 = version.Must(version.NewVersion("1.16.0"))
) )
// checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease // checkServerVersionGreaterThanOrEqual is the canonical way in the SDK to check for versions for API compatibility reasons
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error { func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
if c.ignoreVersion {
return nil
}
if err := c.loadServerVersion(); err != nil { if err := c.loadServerVersion(); err != nil {
return err return err
} }

2
vendor/modules.txt vendored
View file

@ -1,6 +1,6 @@
# 4d63.com/gochecknoglobals v0.1.0 # 4d63.com/gochecknoglobals v0.1.0
4d63.com/gochecknoglobals/checknoglobals 4d63.com/gochecknoglobals/checknoglobals
# code.gitea.io/sdk/gitea v0.15.0 # code.gitea.io/sdk/gitea v0.15.1-0.20220501190934-319a978c6c71
## explicit ## explicit
code.gitea.io/sdk/gitea code.gitea.io/sdk/gitea
# github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e # github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e