forked from argoproj/argo-cd
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of SCM-Manager as scm_provider and pullrequest…
…_generator for applicationsets Signed-off-by: Eduard Heimbuch <[email protected]>
- Loading branch information
Showing
20 changed files
with
2,982 additions
and
828 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package pull_request | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"os" | ||
"strconv" | ||
|
||
scmm "github.com/scm-manager/goscm" | ||
) | ||
|
||
type ScmManagerService struct { | ||
client *scmm.Client | ||
namespace string | ||
name string | ||
} | ||
|
||
var _ PullRequestService = (*ScmManagerService)(nil) | ||
|
||
func NewScmManagerService(ctx context.Context, token, url, namespace, name string, insecure bool) (PullRequestService, error) { | ||
if token == "" { | ||
token = os.Getenv("SCMM_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 := scmm.NewClient(url, token) | ||
if err != nil { | ||
return nil, err | ||
} | ||
client.SetHttpClient(httpClient) | ||
return &ScmManagerService{ | ||
client: client, | ||
namespace: namespace, | ||
name: name, | ||
}, nil | ||
} | ||
|
||
func (g *ScmManagerService) List(ctx context.Context) ([]*PullRequest, error) { | ||
prs, err := g.client.ListPullRequests(g.namespace, g.name, g.client.NewPullRequestListFilter()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
list := []*PullRequest{} | ||
for _, pr := range prs.Embedded.PullRequests { | ||
changeset, err := g.client.GetHeadChangesetForBranch(g.namespace, g.name, pr.Source) | ||
if err != nil { | ||
return nil, err | ||
} | ||
prId, err := strconv.Atoi(pr.Id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
list = append(list, &PullRequest{ | ||
Number: prId, | ||
Branch: pr.Source, | ||
HeadSHA: changeset.Id, | ||
Labels: make([]string, 0), | ||
}) | ||
} | ||
return list, nil | ||
} |
107 changes: 107 additions & 0 deletions
107
applicationset/services/pull_request/scm-manager_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package pull_request | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/stretchr/testify/assert" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
) | ||
|
||
func scmmMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "application/json") | ||
fmt.Println(r.RequestURI) | ||
switch r.RequestURI { | ||
case "/api/v2/pull-requests/test-argocd/pr-test?status=OPEN&pageSize=10": | ||
_, err := io.WriteString(w, `{ | ||
"page": 0, | ||
"pageTotal": 1, | ||
"_embedded": { | ||
"pullRequests": [ | ||
{ | ||
"id": "1", | ||
"author": { | ||
"id": "eheimbuch", | ||
"displayName": "Eduard Heimbuch", | ||
"mail": "[email protected]" | ||
}, | ||
"reviser": { | ||
"id": null, | ||
"displayName": null | ||
}, | ||
"closeDate": null, | ||
"source": "test_pr", | ||
"target": "main", | ||
"title": "New feature xyz", | ||
"description": "Awesome!", | ||
"creationDate": "2023-01-23T12:58:56.770Z", | ||
"lastModified": null, | ||
"status": "OPEN", | ||
"reviewer": [], | ||
"tasks": { | ||
"todo": 0, | ||
"done": 0 | ||
}, | ||
"sourceRevision": null, | ||
"targetRevision": null, | ||
"markedAsReviewed": [], | ||
"emergencyMerged": false, | ||
"ignoredMergeObstacles": null | ||
} | ||
] | ||
} | ||
}`) | ||
if err != nil { | ||
t.Fail() | ||
} | ||
case "/api/v2/repositories/test-argocd/pr-test/branches/test_pr/changesets?&pageSize=1": | ||
_, err := io.WriteString(w, `{ | ||
"page": 0, | ||
"pageTotal": 1, | ||
"_embedded": { | ||
"changesets": [ | ||
{ | ||
"id": "b4ed814b1afe810c4902bc5590c7b09531296679", | ||
"author": { | ||
"mail": "[email protected]", | ||
"name": "Eduard Heimbuch" | ||
}, | ||
"date": "2023-07-03T08:53:15Z", | ||
"description": "test url", | ||
"contributors": [ | ||
{ | ||
"type": "Pushed-by", | ||
"person": { | ||
"mail": "[email protected]", | ||
"name": "Eduard Heimbuch" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
}`) | ||
if err != nil { | ||
t.Fail() | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestScmManagerPrList(t *testing.T) { | ||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
scmmMockHandler(t)(w, r) | ||
})) | ||
defer ts.Close() | ||
host, err := NewScmManagerService(context.Background(), "", ts.URL, "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_pr") | ||
assert.Equal(t, prs[0].HeadSHA, "b4ed814b1afe810c4902bc5590c7b09531296679") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package scm_provider | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"os" | ||
|
||
scmm "github.com/scm-manager/goscm" | ||
) | ||
|
||
type ScmManagerProvider struct { | ||
client *scmm.Client | ||
allBranches bool | ||
} | ||
|
||
var _ SCMProviderService = &ScmManagerProvider{} | ||
|
||
func NewScmManagerProvider(ctx context.Context, token, url string, allBranches, insecure bool) (*ScmManagerProvider, error) { | ||
if token == "" { | ||
token = os.Getenv("SCMM_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 := scmm.NewClient(url, token) | ||
if err != nil { | ||
return nil, fmt.Errorf("error creating a new SCM-Manager client: %w", err) | ||
} | ||
client.SetHttpClient(httpClient) | ||
return &ScmManagerProvider{ | ||
client: client, | ||
allBranches: allBranches, | ||
}, nil | ||
} | ||
|
||
func (g *ScmManagerProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) { | ||
scmmRepo, err := g.client.GetRepo(repo.Organization, repo.Repository) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if !g.allBranches { | ||
defaultBranch, err := g.client.GetDefaultBranch(repo.Organization, repo.Repository) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return []*Repository{ | ||
{ | ||
Organization: repo.Organization, | ||
Repository: repo.Repository, | ||
Branch: defaultBranch.Name, | ||
URL: repo.URL, | ||
SHA: defaultBranch.Revision, | ||
Labels: make([]string, 0), | ||
RepositoryId: scmmRepo.Namespace + "/" + scmmRepo.Name, | ||
}, | ||
}, nil | ||
} | ||
repos := []*Repository{} | ||
branches, err := g.client.ListRepoBranches(repo.Organization, repo.Repository) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, branch := range branches.Embedded.Branches { | ||
repos = append(repos, &Repository{ | ||
Organization: scmmRepo.Namespace, | ||
Repository: scmmRepo.Name, | ||
Branch: branch.Name, | ||
URL: scmmRepo.Links.ProtocolUrl[0].Href, | ||
SHA: branch.Revision, | ||
Labels: make([]string, 0), | ||
RepositoryId: scmmRepo.Namespace + "/" + scmmRepo.Name, | ||
}) | ||
} | ||
return repos, nil | ||
} | ||
|
||
func (g *ScmManagerProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) { | ||
repos := []*Repository{} | ||
filter := g.client.NewRepoListFilter() | ||
filter.Limit = 9999 | ||
scmmRepos, err := g.client.ListRepos(filter) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, scmmRepo := range scmmRepos.Embedded.Repositories { | ||
var url string | ||
switch cloneProtocol { | ||
// Default to SSH if unspecified (i.e. if ""). SSH Plugin needs to be installed | ||
case "", "ssh": | ||
url = getProtocolUrlByName(scmmRepo.Links.ProtocolUrl, "ssh") | ||
case "https": | ||
url = getProtocolUrlByName(scmmRepo.Links.ProtocolUrl, "http") | ||
default: | ||
return nil, fmt.Errorf("unknown clone protocol %v", cloneProtocol) | ||
} | ||
|
||
if url == "" { | ||
return nil, errors.New("could not find valid repository protocol url") | ||
} | ||
|
||
defaultBranch, err := g.client.GetDefaultBranch(scmmRepo.Namespace, scmmRepo.Name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
repos = append(repos, &Repository{ | ||
Organization: scmmRepo.Namespace, | ||
Repository: scmmRepo.Name, | ||
Branch: defaultBranch.Name, | ||
URL: url, | ||
RepositoryId: scmmRepo.Namespace + "/" + scmmRepo.Name, | ||
}) | ||
} | ||
return repos, nil | ||
} | ||
|
||
func getProtocolUrlByName(urls []scmm.ProtocolUrl, name string) string { | ||
for _, url := range urls { | ||
if url.Name == name { | ||
return url.Href | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
func (g *ScmManagerProvider) RepoHasPath(ctx context.Context, repo *Repository, path string) (bool, error) { | ||
_, resp, err := g.client.GetContent(repo.Organization, repo.Repository, repo.Branch, path) | ||
if resp != nil && resp.StatusCode == 404 { | ||
return false, nil | ||
} | ||
if fmt.Sprint(err) == "expect file, got directory" { | ||
return true, nil | ||
} | ||
if err != nil { | ||
return false, err | ||
} | ||
return true, nil | ||
} |
Oops, something went wrong.