Skip to content
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

feat: New SCM and pull request ApplicationSet generators for Gitea #8989

Merged
merged 4 commits into from
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions applicationset/generators/pull_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
return pullrequest.NewGithubService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Labels)
}
if generatorConfig.Gitea != nil {
providerConfig := generatorConfig.Gitea
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
}
return pullrequest.NewGiteaService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Insecure)
}
return nil, fmt.Errorf("no Pull Request provider implementation configured")
}

Expand Down
9 changes: 9 additions & 0 deletions applicationset/generators/scm_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
if err != nil {
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
}
} else if providerConfig.Gitea != nil {
token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Gitea token: %v", err)
}
provider, err = scm_provider.NewGiteaProvider(ctx, providerConfig.Gitea.Owner, token, providerConfig.Gitea.API, providerConfig.Gitea.AllBranches, providerConfig.Gitea.Insecure)
if err != nil {
return nil, fmt.Errorf("error initializing Gitea service: %v", err)
}
} else {
return nil, fmt.Errorf("no SCM provider implementation configured")
}
Expand Down
63 changes: 63 additions & 0 deletions applicationset/services/pull_request/gitea.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package pull_request

import (
"context"
"crypto/tls"
"net/http"
"net/http/cookiejar"
"os"

"code.gitea.io/sdk/gitea"
)

type GiteaService struct {
client *gitea.Client
owner string
repo string
}

var _ PullRequestService = (*GiteaService)(nil)

func NewGiteaService(ctx context.Context, token, url, owner, repo string, insecure bool) (PullRequestService, error) {
if token == "" {
token = os.Getenv("GITEA_TOKEN")
}
httpClient := &http.Client{}
if insecure {
cookieJar, _ := cookiejar.New(nil)

httpClient = &http.Client{
Jar: cookieJar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
}
client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient))
if err != nil {
return nil, err
}
return &GiteaService{
client: client,
owner: owner,
repo: repo,
}, nil
}

func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
opts := gitea.ListPullRequestsOptions{
State: gitea.StateOpen,
}
prs, _, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
if err != nil {
return nil, err
}
list := []*PullRequest{}
for _, pr := range prs {
list = append(list, &PullRequest{
Number: int(pr.Index),
Branch: pr.Head.Ref,
HeadSHA: pr.Head.Sha,
})
}
return list, nil
}
19 changes: 19 additions & 0 deletions applicationset/services/pull_request/gitea_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package pull_request

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGiteaList(t *testing.T) {
host, err := NewGiteaService(context.Background(), "", "https://gitea.com", "test-argocd", "pr-test", false)
assert.Nil(t, err)
prs, err := host.List(context.Background())
assert.Nil(t, err)
assert.Equal(t, len(prs), 1)
assert.Equal(t, prs[0].Number, 1)
assert.Equal(t, prs[0].Branch, "test")
assert.Equal(t, prs[0].HeadSHA, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f")
}
133 changes: 133 additions & 0 deletions applicationset/services/scm_provider/gitea.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package scm_provider

import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/http/cookiejar"
"os"

"code.gitea.io/sdk/gitea"
)

type GiteaProvider struct {
client *gitea.Client
owner string
allBranches bool
}

var _ SCMProviderService = &GiteaProvider{}

func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches, insecure bool) (*GiteaProvider, error) {
if token == "" {
token = os.Getenv("GITEA_TOKEN")
dmolik marked this conversation as resolved.
Show resolved Hide resolved
}
httpClient := &http.Client{}
if insecure {
cookieJar, _ := cookiejar.New(nil)

httpClient = &http.Client{
Jar: cookieJar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
}
client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient))
if err != nil {
return nil, err
}
crenshaw-dev marked this conversation as resolved.
Show resolved Hide resolved
return &GiteaProvider{
client: client,
owner: owner,
allBranches: allBranches,
}, nil
}

func (g *GiteaProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
if !g.allBranches {
branch, _, err := g.client.GetRepoBranch(g.owner, repo.Repository, repo.Branch)
if err != nil {
return nil, err
}
return []*Repository{
{
Organization: repo.Organization,
Repository: repo.Repository,
Branch: repo.Branch,
URL: repo.URL,
SHA: branch.Commit.ID,
Labels: repo.Labels,
RepositoryId: repo.RepositoryId,
},
}, nil
}
repos := []*Repository{}
opts := gitea.ListRepoBranchesOptions{}
branches, _, err := g.client.ListRepoBranches(g.owner, repo.Repository, opts)
if err != nil {
return nil, err
}
for _, branch := range branches {
repos = append(repos, &Repository{
Organization: repo.Organization,
Repository: repo.Repository,
Branch: branch.Name,
URL: repo.URL,
SHA: branch.Commit.ID,
Labels: repo.Labels,
RepositoryId: repo.RepositoryId,
})
}
return repos, nil
}

func (g *GiteaProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) {
repos := []*Repository{}
repoOpts := gitea.ListOrgReposOptions{}
giteaRepos, _, err := g.client.ListOrgRepos(g.owner, repoOpts)
if err != nil {
return nil, err
}
for _, repo := range giteaRepos {
var url string
switch cloneProtocol {
// Default to SSH if unspecified (i.e. if "").
case "", "ssh":
url = repo.SSHURL
case "https":
url = repo.HTMLURL
default:
return nil, fmt.Errorf("unknown clone protocol for GitHub %v", cloneProtocol)
}
labelOpts := gitea.ListLabelsOptions{}
giteaLabels, _, err := g.client.ListRepoLabels(g.owner, repo.Name, labelOpts)
if err != nil {
return nil, err
}
labels := []string{}
for _, label := range giteaLabels {
labels = append(labels, label.Name)
}
repos = append(repos, &Repository{
Organization: g.owner,
Repository: repo.Name,
Branch: repo.DefaultBranch,
URL: url,
Labels: labels,
RepositoryId: int(repo.ID),
})
}
return repos, nil
}

func (g *GiteaProvider) RepoHasPath(ctx context.Context, repo *Repository, path string) (bool, error) {
_, resp, err := g.client.GetContents(repo.Organization, repo.Repository, repo.Branch, path)
if resp != nil && resp.StatusCode == 404 {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}
93 changes: 93 additions & 0 deletions applicationset/services/scm_provider/gitea_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package scm_provider

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/argoproj/argo-cd/v2/pkg/apis/applicationset/v1alpha1"
)

func TestGiteaListRepos(t *testing.T) {
cases := []struct {
name, proto, url string
hasError, allBranches, includeSubgroups bool
branches []string
filters []v1alpha1.SCMProviderGeneratorFilter
}{
{
name: "blank protocol",
allBranches: false,
url: "git@gitea.com:gitea/go-sdk.git",
branches: []string{"master"},
},
{
name: "ssh protocol",
allBranches: false,
proto: "ssh",
url: "git@gitea.com:gitea/go-sdk.git",
},
{
name: "https protocol",
allBranches: false,
proto: "https",
url: "https://gitea.com/gitea/go-sdk",
},
{
name: "other protocol",
allBranches: false,
proto: "other",
hasError: true,
},
{
name: "all branches",
allBranches: true,
url: "git@gitea.com:gitea/go-sdk.git",
branches: []string{"master", "release/v0.11", "release/v0.12", "release/v0.13", "release/v0.14", "release/v0.15"},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
provider, _ := NewGiteaProvider(context.Background(), "gitea", "", "https://gitea.com/", c.allBranches, false)
rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto)
if c.hasError {
assert.NotNil(t, err)
} else {
checkRateLimit(t, err)
assert.Nil(t, err)
// Just check that this one project shows up. Not a great test but better thing nothing?
repos := []*Repository{}
branches := []string{}
for _, r := range rawRepos {
if r.Repository == "go-sdk" {
repos = append(repos, r)
branches = append(branches, r.Branch)
}
}
assert.NotEmpty(t, repos)
assert.Equal(t, c.url, repos[0].URL)
for _, b := range c.branches {
assert.Contains(t, branches, b)
}
}
})
}
}

func TestGiteaHasPath(t *testing.T) {
host, _ := NewGiteaProvider(context.Background(), "gitea", "", "https://gitea.com/", false, false)
repo := &Repository{
Organization: "gitea",
Repository: "go-sdk",
Branch: "master",
}
ok, err := host.RepoHasPath(context.Background(), repo, "README.md")
dmolik marked this conversation as resolved.
Show resolved Hide resolved
assert.Nil(t, err)
assert.True(t, ok)

ok, err = host.RepoHasPath(context.Background(), repo, "notathing")
assert.Nil(t, err)
assert.False(t, ok)
}
37 changes: 37 additions & 0 deletions docs/operator-manual/applicationset/Generators-Pull-Request.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,43 @@ spec:
* `tokenRef`: A `Secret` name and key containing the GitHub access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `labels`: Labels is used to filter the PRs that you want to target. (Optional)

## Gitea

Specify the repository from which to fetch the Gitea Pull requests.

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
spec:
generators:
- pullRequest:
gitea:
# The Gitea organization or user.
owner: myorg
# The Gitea repository
repo: myrepository
# The Gitea url to use
api: https://gitea.mydomain.com/
# Reference to a Secret containing an access token. (optional)
tokenRef:
secretName: gitea-token
key: token
# many gitea deployments use TLS, but many are self-hosted and self-signed certificates
insecure: true
requeueAfterSeconds: 1800
template:
# ...
```

* `owner`: Required name of the Gitea organization or user.
* `repo`: Required name of the Gitea repositry.
* `api`: The url of the Gitea instance.
* `tokenRef`: A `Secret` name and key containing the Gitea access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `insecure`: `Allow for self-signed certificates, primarily for testing.`


## Template

As with all generators, several keys are available for replacement in the generated application.
Expand Down
Loading