Skip to content

Commit 4f92274

Browse files
committed
initial support for gitea
Signed-off-by: Malar Kannan <[email protected]>
1 parent 3be74ba commit 4f92274

28 files changed

+3484
-0
lines changed

gitea/auth.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2020 The Flux CD contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package gitea
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
"code.gitea.io/sdk/gitea"
24+
25+
"github.com/fluxcd/go-git-providers/gitprovider"
26+
)
27+
28+
const (
29+
// DefaultDomain specifies the default domain used as the backend.
30+
DefaultDomain = "gitea.com"
31+
// TokenVariable is the common name for the environment variable
32+
// containing a Gitea authentication token.
33+
TokenVariable = "GITEA_TOKEN" // #nosec G101
34+
)
35+
36+
// NewClient creates a new gitprovider.Client instance for Gitea API endpoints.
37+
//
38+
// Gitea Selfhosted can be used if you specify the domain using WithDomain.
39+
func NewClient(optFns ...gitprovider.ClientOption) (gitprovider.Client, error) {
40+
// Complete the options struct
41+
opts, err := gitprovider.MakeClientOptions(optFns...)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
// Create a *http.Client using the transport chain
47+
httpClient, err := gitprovider.BuildClientFromTransportChain(opts.GetTransportChain())
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
// Create the Gitea client either for the default gitea.com domain, or
53+
// a custom enterprise domain if opts.Domain is set to something other than
54+
// the default.
55+
var gt *gitea.Client
56+
var domain string
57+
58+
// Gitea is primarily self-hosted
59+
// using test domain if domain not provided
60+
domain = *opts.Domain
61+
if opts.Domain == nil || *opts.Domain == DefaultDomain {
62+
// No domain set or the default gitea.com used
63+
domain = DefaultDomain
64+
}
65+
baseURL := domain
66+
if !strings.Contains(domain, "://") {
67+
baseURL = fmt.Sprintf("https://%s/", domain)
68+
}
69+
if gt, err = gitea.NewClient(baseURL, gitea.SetHTTPClient(httpClient)); err != nil {
70+
return nil, err
71+
}
72+
// By default, turn destructive actions off. But allow overrides.
73+
destructiveActions := false
74+
if opts.EnableDestructiveAPICalls != nil {
75+
destructiveActions = *opts.EnableDestructiveAPICalls
76+
}
77+
78+
return newClient(gt, domain, destructiveActions), nil
79+
}

gitea/auth_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Copyright 2020 The Flux CD contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package gitea
18+
19+
import (
20+
"testing"
21+
22+
"github.com/fluxcd/go-git-providers/gitprovider"
23+
)
24+
25+
func Test_DomainVariations(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
opts gitprovider.ClientOption
29+
want string
30+
expectedErrs []error
31+
}{
32+
{
33+
name: "gitea.com domain",
34+
opts: gitprovider.WithDomain("gitea.com"),
35+
want: "gitea.com",
36+
},
37+
// {
38+
// name: "custom domain without protocol",
39+
// opts: gitprovider.WithDomain("my-gitea.dev.com"),
40+
// want: "https://my-gitea.dev.com",
41+
// },
42+
// {
43+
// name: "custom domain with https protocol",
44+
// opts: gitprovider.WithDomain("https://my-gitea.dev.com"),
45+
// want: "https://my-gitea.dev.com",
46+
// },
47+
{
48+
name: "custom domain with http protocol",
49+
opts: gitprovider.WithDomain("http://my-gitea.dev.com"),
50+
want: "http://my-gitea.dev.com",
51+
},
52+
}
53+
for _, tt := range tests {
54+
t.Run(tt.name, func(t *testing.T) {
55+
c1, _ := NewClient(tt.opts)
56+
assertEqual(t, tt.want, c1.SupportedDomain())
57+
58+
c2, _ := NewClient(tt.opts)
59+
assertEqual(t, tt.want, c2.SupportedDomain())
60+
})
61+
}
62+
}
63+
64+
func assertEqual(t *testing.T, a interface{}, b interface{}) {
65+
if a != b {
66+
t.Fatalf("%s != %s", a, b)
67+
}
68+
}

gitea/client.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Copyright 2020 The Flux CD contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package gitea
18+
19+
import (
20+
"context"
21+
22+
"code.gitea.io/sdk/gitea"
23+
"github.com/fluxcd/go-git-providers/gitprovider"
24+
)
25+
26+
// ProviderID is the provider ID for Gitea.
27+
const ProviderID = gitprovider.ProviderID("gitea")
28+
29+
func newClient(c *gitea.Client, domain string, destructiveActions bool) *Client {
30+
gtClient := &giteaClientImpl{c, destructiveActions}
31+
ctx := &clientContext{gtClient, domain, destructiveActions}
32+
return &Client{
33+
clientContext: ctx,
34+
orgs: &OrganizationsClient{
35+
clientContext: ctx,
36+
},
37+
orgRepos: &OrgRepositoriesClient{
38+
clientContext: ctx,
39+
},
40+
userRepos: &UserRepositoriesClient{
41+
clientContext: ctx,
42+
},
43+
}
44+
}
45+
46+
type clientContext struct {
47+
c giteaClient
48+
domain string
49+
destructiveActions bool
50+
}
51+
52+
// Client implements the gitprovider.Client interface.
53+
var _ gitprovider.Client = &Client{}
54+
55+
// Client is an interface that allows talking to a Git provider.
56+
type Client struct {
57+
*clientContext
58+
59+
orgs *OrganizationsClient
60+
orgRepos *OrgRepositoriesClient
61+
userRepos *UserRepositoriesClient
62+
}
63+
64+
// SupportedDomain returns the domain endpoint for this client, e.g. "gitea.com", "gitea.dev.com" or
65+
// "my-custom-git-server.com:6443". This allows a higher-level user to know what Client to use for
66+
// what endpoints.
67+
// This field is set at client creation time, and can't be changed.
68+
func (c *Client) SupportedDomain() string {
69+
return c.domain
70+
}
71+
72+
// ProviderID returns the provider ID "gitea".
73+
// This field is set at client creation time, and can't be changed.
74+
func (c *Client) ProviderID() gitprovider.ProviderID {
75+
return ProviderID
76+
}
77+
78+
// Raw returns the Gitea client (code.gitea.io/sdk/gitea *Client)
79+
// used under the hood for accessing Gitea.
80+
func (c *Client) Raw() interface{} {
81+
return c.c.Client()
82+
}
83+
84+
// Organizations returns the OrganizationsClient handling sets of organizations.
85+
func (c *Client) Organizations() gitprovider.OrganizationsClient {
86+
return c.orgs
87+
}
88+
89+
// OrgRepositories returns the OrgRepositoriesClient handling sets of repositories in an organization.
90+
func (c *Client) OrgRepositories() gitprovider.OrgRepositoriesClient {
91+
return c.orgRepos
92+
}
93+
94+
// UserRepositories returns the UserRepositoriesClient handling sets of repositories for a user.
95+
func (c *Client) UserRepositories() gitprovider.UserRepositoriesClient {
96+
return c.userRepos
97+
}
98+
99+
//nolint:gochecknoglobals
100+
var permissionScopes = map[gitprovider.TokenPermission]string{
101+
gitprovider.TokenPermissionRWRepository: "repo",
102+
}
103+
104+
// HasTokenPermission returns true if the given token has the given permissions.
105+
func (c *Client) HasTokenPermission(ctx context.Context, permission gitprovider.TokenPermission) (bool, error) {
106+
return false, gitprovider.ErrNoProviderSupport
107+
}

gitea/client_organization_teams.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2020 The Flux CD contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package gitea
18+
19+
import (
20+
"context"
21+
22+
"code.gitea.io/sdk/gitea"
23+
"github.com/fluxcd/go-git-providers/gitprovider"
24+
)
25+
26+
// TeamsClient implements the gitprovider.TeamsClient interface.
27+
var _ gitprovider.TeamsClient = &TeamsClient{}
28+
29+
// TeamsClient handles teams organization-wide.
30+
type TeamsClient struct {
31+
*clientContext
32+
ref gitprovider.OrganizationRef
33+
}
34+
35+
// Get a team within the specific organization.
36+
//
37+
// teamName may include slashes, to point to e.g. subgroups in GitLab.
38+
// teamName must not be an empty string.
39+
//
40+
// ErrNotFound is returned if the resource does not exist.
41+
func (c *TeamsClient) Get(ctx context.Context, teamName string) (gitprovider.Team, error) {
42+
// GET /orgs/{org}/teams/{team_slug}/members
43+
apiObjs, err := c.c.ListOrgTeamMembers(ctx, c.ref.Organization, teamName)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
// Collect a list of the members' names. Login is validated to be non-nil in ListOrgTeamMembers.
49+
logins := make([]string, 0, len(apiObjs))
50+
for _, apiObj := range apiObjs {
51+
// Login is validated to be non-nil in ListOrgTeamMembers
52+
logins = append(logins, apiObj.UserName)
53+
}
54+
55+
return &team{
56+
users: apiObjs,
57+
info: gitprovider.TeamInfo{
58+
Name: teamName,
59+
Members: logins,
60+
},
61+
ref: c.ref,
62+
}, nil
63+
}
64+
65+
// List all teams (recursively, in terms of subgroups) within the specific organization.
66+
//
67+
// List returns all available organizations, using multiple paginated requests if needed.
68+
func (c *TeamsClient) List(ctx context.Context) ([]gitprovider.Team, error) {
69+
// GET /orgs/{org}/teams
70+
apiObjs, err := c.c.ListOrgTeams(ctx, c.ref.Organization)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
// Use .Get() to get detailed information about each member
76+
teams := make([]gitprovider.Team, 0, len(apiObjs))
77+
for _, apiObj := range apiObjs {
78+
// Get detailed information about individual teams (including members).
79+
// Slug is validated to be non-nil in ListOrgTeams.
80+
team, err := c.Get(ctx, apiObj.Name)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
teams = append(teams, team)
86+
}
87+
88+
return teams, nil
89+
}
90+
91+
var _ gitprovider.Team = &team{}
92+
93+
type team struct {
94+
users []*gitea.User
95+
info gitprovider.TeamInfo
96+
ref gitprovider.OrganizationRef
97+
}
98+
99+
func (t *team) Get() gitprovider.TeamInfo {
100+
return t.info
101+
}
102+
103+
func (t *team) APIObject() interface{} {
104+
return t.users
105+
}
106+
107+
func (t *team) Organization() gitprovider.OrganizationRef {
108+
return t.ref
109+
}

0 commit comments

Comments
 (0)