Skip to content

initial support for gitea #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions gitea/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2020 The Flux CD contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitea

import (
"fmt"
"strings"

"code.gitea.io/sdk/gitea"

"github.com/fluxcd/go-git-providers/gitprovider"
)

const (
// DefaultDomain specifies the default domain used as the backend.
DefaultDomain = "gitea.com"
)

// NewClient creates a new gitprovider.Client instance for Gitea API endpoints.
//
// Gitea Selfhosted can be used if you specify the domain using WithDomain.
func NewClient(optFns ...gitprovider.ClientOption) (gitprovider.Client, error) {
// Complete the options struct
opts, err := gitprovider.MakeClientOptions(optFns...)
if err != nil {
return nil, err
}

// Create a *http.Client using the transport chain
httpClient, err := gitprovider.BuildClientFromTransportChain(opts.GetTransportChain())
if err != nil {
return nil, err
}

// Create the Gitea client either for the default gitea.com domain, or
// a custom enterprise domain if opts.Domain is set to something other than
// the default.
var gt *gitea.Client
var domain string

// Gitea is primarily self-hosted
// using default domain if domain not provided
domain = *opts.Domain
if opts.Domain == nil || *opts.Domain == DefaultDomain {
// No domain set or the default gitea.com used
domain = DefaultDomain
}
baseURL := domain
if !strings.Contains(domain, "://") {
baseURL = fmt.Sprintf("https://%s/", domain)
}
if gt, err = gitea.NewClient(baseURL, gitea.SetHTTPClient(httpClient)); err != nil {
return nil, err
}
// By default, turn destructive actions off. But allow overrides.
destructiveActions := false
if opts.EnableDestructiveAPICalls != nil {
destructiveActions = *opts.EnableDestructiveAPICalls
}

return newClient(gt, domain, destructiveActions), nil
}
68 changes: 68 additions & 0 deletions gitea/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2020 The Flux CD contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitea

import (
"testing"

"github.com/fluxcd/go-git-providers/gitprovider"
)

func Test_DomainVariations(t *testing.T) {
tests := []struct {
name string
opts gitprovider.ClientOption
want string
expectedErrs []error
}{
{
name: "gitea.com domain",
opts: gitprovider.WithDomain("gitea.com"),
want: "gitea.com",
},
{
name: "custom domain without protocol",
opts: gitprovider.WithDomain("my-gitea.dev.com"),
want: "https://my-gitea.dev.com",
},
{
name: "custom domain with https protocol",
opts: gitprovider.WithDomain("https://my-gitea.dev.com"),
want: "https://my-gitea.dev.com",
},
{
name: "custom domain with http protocol",
opts: gitprovider.WithDomain("http://my-gitea.dev.com"),
want: "http://my-gitea.dev.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c1, _ := NewClient(tt.opts)
assertEqual(t, tt.want, c1.SupportedDomain())

c2, _ := NewClient(tt.opts)
assertEqual(t, tt.want, c2.SupportedDomain())
})
}
}

func assertEqual(t *testing.T, a interface{}, b interface{}) {
if a != b {
t.Fatalf("%s != %s", a, b)
}
}
107 changes: 107 additions & 0 deletions gitea/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Copyright 2020 The Flux CD contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitea

import (
"context"

"code.gitea.io/sdk/gitea"
"github.com/fluxcd/go-git-providers/gitprovider"
)

// ProviderID is the provider ID for Gitea.
const ProviderID = gitprovider.ProviderID("gitea")

func newClient(c *gitea.Client, domain string, destructiveActions bool) *Client {
gtClient := &giteaClientImpl{c, destructiveActions}
ctx := &clientContext{gtClient, domain, destructiveActions}
return &Client{
clientContext: ctx,
orgs: &OrganizationsClient{
clientContext: ctx,
},
orgRepos: &OrgRepositoriesClient{
clientContext: ctx,
},
userRepos: &UserRepositoriesClient{
clientContext: ctx,
},
}
}

type clientContext struct {
c giteaClient
domain string
destructiveActions bool
}

// Client implements the gitprovider.Client interface.
var _ gitprovider.Client = &Client{}

// Client is an interface that allows talking to a Git provider.
type Client struct {
*clientContext

orgs *OrganizationsClient
orgRepos *OrgRepositoriesClient
userRepos *UserRepositoriesClient
}

// SupportedDomain returns the domain endpoint for this client, e.g. "gitea.com", "gitea.dev.com" or
// "my-custom-git-server.com:6443". This allows a higher-level user to know what Client to use for
// what endpoints.
// This field is set at client creation time, and can't be changed.
func (c *Client) SupportedDomain() string {
return c.domain
}

// ProviderID returns the provider ID "gitea".
// This field is set at client creation time, and can't be changed.
func (c *Client) ProviderID() gitprovider.ProviderID {
return ProviderID
}

// Raw returns the Gitea client (code.gitea.io/sdk/gitea *Client)
// used under the hood for accessing Gitea.
func (c *Client) Raw() interface{} {
return c.c.Client()
}

// Organizations returns the OrganizationsClient handling sets of organizations.
func (c *Client) Organizations() gitprovider.OrganizationsClient {
return c.orgs
}

// OrgRepositories returns the OrgRepositoriesClient handling sets of repositories in an organization.
func (c *Client) OrgRepositories() gitprovider.OrgRepositoriesClient {
return c.orgRepos
}

// UserRepositories returns the UserRepositoriesClient handling sets of repositories for a user.
func (c *Client) UserRepositories() gitprovider.UserRepositoriesClient {
return c.userRepos
}

//nolint:gochecknoglobals
var permissionScopes = map[gitprovider.TokenPermission]string{
gitprovider.TokenPermissionRWRepository: "repo",
}

// HasTokenPermission returns true if the given token has the given permissions.
func (c *Client) HasTokenPermission(ctx context.Context, permission gitprovider.TokenPermission) (bool, error) {
return false, gitprovider.ErrNoProviderSupport
}
109 changes: 109 additions & 0 deletions gitea/client_organization_teams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2020 The Flux CD contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitea

import (
"context"

"code.gitea.io/sdk/gitea"
"github.com/fluxcd/go-git-providers/gitprovider"
)

// TeamsClient implements the gitprovider.TeamsClient interface.
var _ gitprovider.TeamsClient = &TeamsClient{}

// TeamsClient handles teams organization-wide.
type TeamsClient struct {
*clientContext
ref gitprovider.OrganizationRef
}

// Get a team within the specific organization.
//
// teamName may include slashes, to point to e.g. subgroups in GitLab.
// teamName must not be an empty string.
//
// ErrNotFound is returned if the resource does not exist.
func (c *TeamsClient) Get(ctx context.Context, teamName string) (gitprovider.Team, error) {
// GET /orgs/{org}/teams/{team_slug}/members
apiObjs, err := c.c.ListOrgTeamMembers(ctx, c.ref.Organization, teamName)
if err != nil {
return nil, err
}

// Collect a list of the members' names. Login is validated to be non-nil in ListOrgTeamMembers.
logins := make([]string, 0, len(apiObjs))
for _, apiObj := range apiObjs {
// Login is validated to be non-nil in ListOrgTeamMembers
logins = append(logins, apiObj.UserName)
}

return &team{
users: apiObjs,
info: gitprovider.TeamInfo{
Name: teamName,
Members: logins,
},
ref: c.ref,
}, nil
}

// List all teams (recursively, in terms of subgroups) within the specific organization.
//
// List returns all available organizations, using multiple paginated requests if needed.
func (c *TeamsClient) List(ctx context.Context) ([]gitprovider.Team, error) {
// GET /orgs/{org}/teams
apiObjs, err := c.c.ListOrgTeams(ctx, c.ref.Organization)
if err != nil {
return nil, err
}

// Use .Get() to get detailed information about each member
teams := make([]gitprovider.Team, 0, len(apiObjs))
for _, apiObj := range apiObjs {
// Get detailed information about individual teams (including members).
// Slug is validated to be non-nil in ListOrgTeams.
team, err := c.Get(ctx, apiObj.Name)
if err != nil {
return nil, err
}

teams = append(teams, team)
}

return teams, nil
}

var _ gitprovider.Team = &team{}

type team struct {
users []*gitea.User
info gitprovider.TeamInfo
ref gitprovider.OrganizationRef
}

func (t *team) Get() gitprovider.TeamInfo {
return t.info
}

func (t *team) APIObject() interface{} {
return t.users
}

func (t *team) Organization() gitprovider.OrganizationRef {
return t.ref
}
Loading