diff --git a/gitea/client.go b/gitea/client.go index 3f1d618..64d3c79 100644 --- a/gitea/client.go +++ b/gitea/client.go @@ -30,18 +30,20 @@ func Version() string { // Client represents a thread-safe Gitea API client. type Client struct { - url string - accessToken string - username string - password string - otp string - sudo string - debug bool - client *http.Client - ctx context.Context - mutex sync.RWMutex + url string + accessToken string + username string + password string + otp string + sudo string + debug bool + client *http.Client + ctx context.Context + mutex sync.RWMutex + serverVersion *version.Version getVersionOnce sync.Once + ignoreVersion bool // only set by SetGiteaVersion so don't need a mutex lock } // Response represents the gitea response @@ -49,16 +51,21 @@ type Response struct { *http.Response } +// ClientOption are functions used to init a new client +type ClientOption func(*Client) error + // NewClient initializes and returns a API client. // 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{ url: strings.TrimSuffix(url, "/"), client: &http.Client{}, ctx: context.Background(), } for _, opt := range options { - opt(client) + if err := opt(client); err != nil { + return nil, err + } } if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { return nil, err @@ -74,9 +81,10 @@ func NewClientWithHTTP(url string, httpClient *http.Client) *Client { } // SetHTTPClient is an option for NewClient to set custom http client -func SetHTTPClient(httpClient *http.Client) func(client *Client) { - return func(client *Client) { +func SetHTTPClient(httpClient *http.Client) ClientOption { + return func(client *Client) error { client.SetHTTPClient(httpClient) + return nil } } @@ -88,18 +96,20 @@ func (c *Client) SetHTTPClient(client *http.Client) { } // SetToken is an option for NewClient to set token -func SetToken(token string) func(client *Client) { - return func(client *Client) { +func SetToken(token string) ClientOption { + return func(client *Client) error { client.mutex.Lock() client.accessToken = token client.mutex.Unlock() + return nil } } // SetBasicAuth is an option for NewClient to set username and password -func SetBasicAuth(username, password string) func(client *Client) { - return func(client *Client) { +func SetBasicAuth(username, password string) ClientOption { + return func(client *Client) error { client.SetBasicAuth(username, password) + return nil } } @@ -111,9 +121,10 @@ func (c *Client) SetBasicAuth(username, password string) { } // SetOTP is an option for NewClient to set OTP for 2FA -func SetOTP(otp string) func(client *Client) { - return func(client *Client) { +func SetOTP(otp string) ClientOption { + return func(client *Client) error { client.SetOTP(otp) + return nil } } @@ -124,14 +135,15 @@ func (c *Client) SetOTP(otp string) { c.mutex.Unlock() } -// SetContext is an option for NewClient to set context -func SetContext(ctx context.Context) func(client *Client) { - return func(client *Client) { +// SetContext is an option for NewClient to set the default context +func SetContext(ctx context.Context) ClientOption { + return func(client *Client) error { 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) { c.mutex.Lock() c.ctx = ctx @@ -139,9 +151,10 @@ func (c *Client) SetContext(ctx context.Context) { } // SetSudo is an option for NewClient to set sudo header -func SetSudo(sudo string) func(client *Client) { - return func(client *Client) { +func SetSudo(sudo string) ClientOption { + return func(client *Client) error { client.SetSudo(sudo) + return nil } } @@ -153,11 +166,12 @@ func (c *Client) SetSudo(sudo string) { } // SetDebugMode is an option for NewClient to enable debug mode -func SetDebugMode() func(client *Client) { - return func(client *Client) { +func SetDebugMode() ClientOption { + return func(client *Client) error { client.mutex.Lock() client.debug = true client.mutex.Unlock() + return nil } } diff --git a/gitea/issue_milestone.go b/gitea/issue_milestone.go index a865a45..dfb5720 100644 --- a/gitea/issue_milestone.go +++ b/gitea/issue_milestone.go @@ -73,7 +73,7 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Respon // GetMilestoneByName get one milestone by repo and milestone name func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone, *Response, error) { - if c.CheckServerVersionConstraint(">=1.13") != nil { + if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { // backwards compatibility mode m, resp, err := c.resolveMilestoneByName(owner, repo, name) return m, resp, err @@ -164,7 +164,7 @@ func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOp // EditMilestoneByName modify milestone with options func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMilestoneOption) (*Milestone, *Response, error) { - if c.CheckServerVersionConstraint(">=1.13") != nil { + if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { // backwards compatibility mode m, _, err := c.resolveMilestoneByName(owner, repo, name) if err != nil { @@ -198,7 +198,7 @@ func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error // DeleteMilestoneByName delete one milestone by name func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Response, error) { - if c.CheckServerVersionConstraint(">=1.13") != nil { + if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { // backwards compatibility mode m, _, err := c.resolveMilestoneByName(owner, repo, name) if err != nil { diff --git a/gitea/main_test.go b/gitea/main_test.go index 611ec6b..0e55e13 100644 --- a/gitea/main_test.go +++ b/gitea/main_test.go @@ -40,14 +40,16 @@ func enableRunGitea() bool { } func newTestClient() *Client { + c, _ := NewClient(getGiteaURL(), newTestClientAuth()) + return c +} + +func newTestClientAuth() ClientOption { token := getGiteaToken() if token == "" { - client := NewClientWithHTTP(getGiteaURL(), &http.Client{}) - client.SetBasicAuth(getGiteaUsername(), getGiteaPassword()) - return client + return SetBasicAuth(getGiteaUsername(), getGiteaPassword()) } - c, _ := NewClient(getGiteaURL(), SetToken(getGiteaToken())) - return c + return SetToken(getGiteaToken()) } func giteaMasterPath() string { diff --git a/gitea/pull.go b/gitea/pull.go index 90a85de..6afebc0 100644 --- a/gitea/pull.go +++ b/gitea/pull.go @@ -12,6 +12,8 @@ import ( "net/url" "strings" "time" + + "github.com/hashicorp/go-version" ) // PRBranchInfo information about a branch @@ -217,10 +219,12 @@ type MergePullRequestOption struct { ForceMerge bool `json:"force_merge"` } +var version1_11_5, _ = version.NewVersion("1.11.5") + // Validate the MergePullRequestOption struct func (opt MergePullRequestOption) Validate(c *Client) error { if opt.Style == MergeStyleSquash { - if err := c.CheckServerVersionConstraint(">=1.11.5"); err != nil { + if err := c.checkServerVersionGreaterThanOrEqual(version1_11_5); err != nil { return err } } diff --git a/gitea/repo.go b/gitea/repo.go index 9c669ae..62d13a3 100644 --- a/gitea/repo.go +++ b/gitea/repo.go @@ -335,7 +335,7 @@ func (opt CreateRepoOption) Validate(c *Client) error { return fmt.Errorf("name has more than 100 chars") } 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 } } diff --git a/gitea/user_app.go b/gitea/user_app.go index 2921eea..88b01ed 100644 --- a/gitea/user_app.go +++ b/gitea/user_app.go @@ -77,7 +77,7 @@ func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) { case reflect.Int64: token = fmt.Sprintf("%d", value.(int64)) case reflect.String: - if err := c.CheckServerVersionConstraint(">= 1.13.0"); err != nil { + if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { return nil, err } token = value.(string) diff --git a/gitea/version.go b/gitea/version.go index 449ad97..1f41c77 100644 --- a/gitea/version.go +++ b/gitea/version.go @@ -39,6 +39,25 @@ func (c *Client) CheckServerVersionConstraint(constraint string) error { 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 + }) + return + } +} + // predefined versions only have to be parsed by library once var ( version1_11_0, _ = version.NewVersion("1.11.0") @@ -48,8 +67,11 @@ var ( version1_15_0, _ = version.NewVersion("1.15.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 { + if c.ignoreVersion { + return nil + } if err := c.loadServerVersion(); err != nil { return err } diff --git a/gitea/version_test.go b/gitea/version_test.go index cfa620e..7383649 100644 --- a/gitea/version_test.go +++ b/gitea/version_test.go @@ -18,6 +18,15 @@ func TestVersion(t *testing.T) { assert.NoError(t, err) assert.True(t, true, rawVersion != "") - assert.NoError(t, c.CheckServerVersionConstraint(">= 1.11.0")) + assert.NoError(t, c.checkServerVersionGreaterThanOrEqual(version1_11_0)) assert.Error(t, c.CheckServerVersionConstraint("< 1.11.0")) + + c.serverVersion = version1_11_0 + assert.Error(t, c.checkServerVersionGreaterThanOrEqual(version1_15_0)) + c.ignoreVersion = true + assert.NoError(t, c.checkServerVersionGreaterThanOrEqual(version1_15_0)) + + c, err = NewClient(getGiteaURL(), newTestClientAuth(), SetGiteaVersion("1.12.123")) + assert.NoError(t, err) + assert.NoError(t, c.CheckServerVersionConstraint("=1.12.123")) }