diff --git a/forgejo/go.mod b/forgejo/go.mod index d0c841f..40a71ea 100644 --- a/forgejo/go.mod +++ b/forgejo/go.mod @@ -7,12 +7,12 @@ require ( github.com/go-fed/httpsig v1.1.0 github.com/hashicorp/go-version v1.6.0 github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.22.0 ) require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.19.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/forgejo/go.sum b/forgejo/go.sum index 3469c7e..4d6c8e8 100644 --- a/forgejo/go.sum +++ b/forgejo/go.sum @@ -14,18 +14,17 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/forgejo/org.go b/forgejo/org.go index 999fd02..e5e2a3a 100644 --- a/forgejo/org.go +++ b/forgejo/org.go @@ -97,7 +97,7 @@ func (opt CreateOrgOption) Validate() error { return fmt.Errorf("empty org name") } if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) { - return fmt.Errorf("infalid bisibility option") + return fmt.Errorf("invalid visibility option") } return nil } @@ -128,7 +128,7 @@ type EditOrgOption struct { // Validate the EditOrgOption struct func (opt EditOrgOption) Validate() error { if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) { - return fmt.Errorf("infalid bisibility option") + return fmt.Errorf("invalid visibility option") } return nil } diff --git a/forgejo/org_action.go b/forgejo/org_action.go index 4a78632..2e8ce53 100644 --- a/forgejo/org_action.go +++ b/forgejo/org_action.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgjo Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -9,16 +9,19 @@ package forgejo import ( + "bytes" + "encoding/json" "fmt" + "net/http" "net/url" ) -// ListOrgMembershipOption list OrgMembership options +// ListOrgActionSecretOption list OrgActionSecret options type ListOrgActionSecretOption struct { ListOptions } -// ListOrgMembership list an organization's members +// ListOrgActionSecret list an organization's secrets func (c *Client) ListOrgActionSecret(org string, opt ListOrgActionSecretOption) ([]*Secret, *Response, error) { if err := escapeValidatePathSegments(&org); err != nil { return nil, nil, err @@ -31,3 +34,58 @@ func (c *Client) ListOrgActionSecret(org string, opt ListOrgActionSecretOption) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &secrets) return secrets, resp, err } + +// CreateSecretOption represents the options for creating a secret. +type CreateSecretOption struct { + Name string `json:"name"` // Name is the name of the secret. + Data string `json:"data"` // Data is the data of the secret. +} + +// Validate checks if the CreateSecretOption is valid. +// It returns an error if any of the validation checks fail. +func (opt *CreateSecretOption) Validate() error { + if len(opt.Name) == 0 { + return fmt.Errorf("name required") + } + if len(opt.Name) > 30 { + return fmt.Errorf("name to long") + } + if len(opt.Data) == 0 { + return fmt.Errorf("data required") + } + return nil +} + +// CreateOrgActionSecret creates a secret for the specified organization in the Gitea Actions. +// It takes the organization name and the secret options as parameters. +// The function returns the HTTP response and an error, if any. +func (c *Client) CreateOrgActionSecret(org string, opt CreateSecretOption) (*Response, error) { + if err := escapeValidatePathSegments(&org); err != nil { + return nil, err + } + if err := (&opt).Validate(); err != nil { + return nil, err + } + body, err := json.Marshal(&opt) + if err != nil { + return nil, err + } + + status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/actions/secrets/%s", org, opt.Name), jsonHeader, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + switch status { + case http.StatusCreated: + return resp, nil + case http.StatusNoContent: + return resp, nil + case http.StatusNotFound: + return resp, fmt.Errorf("forbidden") + case http.StatusBadRequest: + return resp, fmt.Errorf("bad request") + default: + return resp, fmt.Errorf("unexpected Status: %d", status) + } +} diff --git a/forgejo/org_action_test.go b/forgejo/org_action_test.go new file mode 100644 index 0000000..c910e62 --- /dev/null +++ b/forgejo/org_action_test.go @@ -0,0 +1,43 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Copyright 2024 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 forgejo + +import ( + "log" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateOrgActionSecret(t *testing.T) { + log.Println("== TestCreateOrgActionSecret ==") + c := newTestClient() + + user := createTestUser(t, "org_action_user", c) + c.SetSudo(user.UserName) + newOrg, _, err := c.CreateOrg(CreateOrgOption{Name: "ActionOrg"}) + assert.NoError(t, err) + assert.NotNil(t, newOrg) + + // create secret + resp, err := c.CreateOrgActionSecret(newOrg.UserName, CreateSecretOption{Name: "test", Data: "test"}) + assert.NoError(t, err) + assert.Equal(t, http.StatusCreated, resp.StatusCode) + + // update secret + resp, err = c.CreateOrgActionSecret(newOrg.UserName, CreateSecretOption{Name: "test", Data: "test2"}) + assert.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + // list secrets + secrets, _, err := c.ListOrgActionSecret(newOrg.UserName, ListOrgActionSecretOption{}) + assert.NoError(t, err) + assert.Len(t, secrets, 1) +} diff --git a/forgejo/repo_action.go b/forgejo/repo_action.go new file mode 100644 index 0000000..05b9043 --- /dev/null +++ b/forgejo/repo_action.go @@ -0,0 +1,33 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Copyright 2024 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 forgejo + +import ( + "fmt" + "net/url" +) + +// ListRepoActionSecretOption list RepoActionSecret options +type ListRepoActionSecretOption struct { + ListOptions +} + +// ListRepoActionSecret list a repository's secrets +func (c *Client) ListRepoActionSecret(user, repo string, opt ListRepoActionSecretOption) ([]*Secret, *Response, error) { + if err := escapeValidatePathSegments(&user, &repo); err != nil { + return nil, nil, err + } + opt.setDefaults() + secrets := make([]*Secret, 0, opt.PageSize) + + link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/actions/secrets", user, repo)) + link.RawQuery = opt.getURLQuery().Encode() + resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &secrets) + return secrets, resp, err +} diff --git a/forgejo/repo_compare.go b/forgejo/repo_compare.go new file mode 100644 index 0000000..f2c2386 --- /dev/null +++ b/forgejo/repo_compare.go @@ -0,0 +1,37 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Copyright 2024 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 forgejo + +import "fmt" + +// Compare represents a comparison between two commits. +type Compare struct { + TotalCommits int `json:"total_commits"` // Total number of commits in the comparison. + Commits []*Commit `json:"commits"` // List of commits in the comparison. +} + +// CompareCommits compares two commits in a repository. +func (c *Client) CompareCommits(user, repo, prev, current string) (*Compare, *Response, error) { + if err := c.checkServerVersionGreaterThanOrEqual(version1_22_0); err != nil { + return nil, nil, err + } + if err := escapeValidatePathSegments(&user, &repo, &prev, ¤t); err != nil { + return nil, nil, err + } + + basehead := fmt.Sprintf("%s...%s", prev, current) + + apiResp := new(Compare) + resp, err := c.getParsedResponse( + "GET", + fmt.Sprintf("/repos/%s/%s/compare/%s", user, repo, basehead), + nil, nil, apiResp, + ) + return apiResp, resp, err +} diff --git a/forgejo/secret.go b/forgejo/secret.go index 94054c2..22cee7f 100644 --- a/forgejo/secret.go +++ b/forgejo/secret.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgjo Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -13,6 +13,8 @@ import "time" type Secret struct { // the secret's name Name string `json:"name"` + // the secret's data + Data string `json:"data"` // Date and Time of secret creation Created time.Time `json:"created_at"` } diff --git a/forgejo/user.go b/forgejo/user.go index c838c97..a3fed5b 100644 --- a/forgejo/user.go +++ b/forgejo/user.go @@ -21,6 +21,10 @@ type User struct { ID int64 `json:"id"` // the user's username UserName string `json:"login"` + // The login_name of non local users (e.g. LDAP / OAuth / SMTP) + LoginName string `json:"login_name"` + // The ID of the Authentication Source for non local users. + SourceID int64 `json:"source_id"` // the user's full name FullName string `json:"full_name"` Email string `json:"email"` diff --git a/forgejo/version.go b/forgejo/version.go index dc42117..7bec427 100644 --- a/forgejo/version.go +++ b/forgejo/version.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgjo Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -73,6 +73,7 @@ var ( version1_15_0 = version.Must(version.NewVersion("1.15.0")) version1_16_0 = version.Must(version.NewVersion("1.16.0")) version1_17_0 = version.Must(version.NewVersion("1.17.0")) + version1_22_0 = version.Must(version.NewVersion("1.22.0")) ) // ErrUnknownVersion is an unknown version from the API @@ -81,14 +82,13 @@ type ErrUnknownVersion struct { } // Error fulfills error -func (e ErrUnknownVersion) Error() string { +func (e *ErrUnknownVersion) Error() string { return fmt.Sprintf("unknown version: %s", e.raw) } -func (ErrUnknownVersion) Is(target error) bool { - _, ok1 := target.(*ErrUnknownVersion) - _, ok2 := target.(ErrUnknownVersion) - return ok1 || ok2 +func (*ErrUnknownVersion) Is(target error) bool { + _, ok := target.(*ErrUnknownVersion) + return ok } // checkServerVersionGreaterThanOrEqual is the canonical way in the SDK to check for versions for API compatibility reasons