-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Define new git provider interface
Signed-off-by: Balazs Nadasdi <balazs@weave.works>
- Loading branch information
1 parent
6461fee
commit 5a61ac3
Showing
9 changed files
with
1,053 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package git | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/go-logr/logr" | ||
) | ||
|
||
const BitBucketServerProviderName string = "bitbucket-server" | ||
|
||
// BitBucketServerProvider is used to interact with the BitBucket Server (stash) API. | ||
type BitBucketServerProvider struct { | ||
log logr.Logger | ||
} | ||
|
||
func NewBitBucketServerProvider(log logr.Logger) (Provider, error) { | ||
return &BitBucketServerProvider{ | ||
log: log, | ||
}, nil | ||
} | ||
|
||
func (p *BitBucketServerProvider) CreatePullRequest(ctx context.Context, input PullRequestInput) (*PullRequest, error) { | ||
repoURL, err := GetGitProviderUrl(input.RepositoryURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get git provider url: %w", err) | ||
} | ||
|
||
repo, err := GetRepository(ctx, p.log, input.GitProvider, repoURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get repo: %w", err) | ||
} | ||
|
||
// Add the files to be created to the map of changes. | ||
commits := []Commit{} | ||
commits = append(commits, Commit{ | ||
CommitMessage: input.CommitMessage, | ||
Files: input.Files, | ||
}) | ||
|
||
if err := writeFilesToBranch(ctx, p.log, writeFilesToBranchRequest{ | ||
Repository: repo, | ||
HeadBranch: input.Head, | ||
BaseBranch: input.Base, | ||
Commits: commits, | ||
}); err != nil { | ||
return nil, fmt.Errorf("unable to write files to branch %q: %w", input.Head, err) | ||
} | ||
|
||
res, err := createPullRequest(ctx, p.log, createPullRequestRequest{ | ||
Repository: repo, | ||
HeadBranch: input.Head, | ||
BaseBranch: input.Base, | ||
Title: input.Title, | ||
Description: input.Body, | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to create pull request for branch %q: %w", input.Head, err) | ||
} | ||
|
||
return &PullRequest{ | ||
Link: res.WebURL, | ||
}, nil | ||
} |
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,34 @@ | ||
package git | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/go-logr/logr" | ||
) | ||
|
||
// ProviderFactory is used to create and return a | ||
// concrete git provider. | ||
type ProviderFactory struct { | ||
log logr.Logger | ||
} | ||
|
||
// NewFactory creates a new factory for git providers. | ||
func NewFactory(log logr.Logger) *ProviderFactory { | ||
return &ProviderFactory{ | ||
log: log, | ||
} | ||
} | ||
|
||
// Create creates and returns a new git provider. | ||
func (f *ProviderFactory) Create(provider string) (Provider, error) { | ||
switch provider { | ||
case GitHubProviderName: | ||
return NewGitHubProvider(f.log) | ||
case GitLabProviderName: | ||
return NewGitLabProvider(f.log) | ||
case BitBucketServerProviderName: | ||
return NewBitBucketServerProvider(f.log) | ||
default: | ||
return nil, fmt.Errorf("provider %q is not supported", provider) | ||
} | ||
} |
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,66 @@ | ||
package git | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/go-logr/logr" | ||
) | ||
|
||
const GitHubProviderName string = "github" | ||
|
||
// GitHubProvider is used to interact with the GitHub API. | ||
// This implementation delegates most of the work to the | ||
// fluxcd/go-git-providers library. | ||
type GitHubProvider struct { | ||
log logr.Logger | ||
} | ||
|
||
func NewGitHubProvider(log logr.Logger) (Provider, error) { | ||
return &GitHubProvider{ | ||
log: log, | ||
}, nil | ||
} | ||
|
||
func (p *GitHubProvider) CreatePullRequest(ctx context.Context, input PullRequestInput) (*PullRequest, error) { | ||
repoURL, err := GetGitProviderUrl(input.RepositoryURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get git provider url: %w", err) | ||
} | ||
|
||
repo, err := GetRepository(ctx, p.log, input.GitProvider, repoURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get repo: %w", err) | ||
} | ||
|
||
// Add the files to be created to the map of changes. | ||
commits := []Commit{} | ||
commits = append(commits, Commit{ | ||
CommitMessage: input.CommitMessage, | ||
Files: input.Files, | ||
}) | ||
|
||
if err := writeFilesToBranch(ctx, p.log, writeFilesToBranchRequest{ | ||
Repository: repo, | ||
HeadBranch: input.Head, | ||
BaseBranch: input.Base, | ||
Commits: commits, | ||
}); err != nil { | ||
return nil, fmt.Errorf("unable to write files to branch %q: %w", input.Head, err) | ||
} | ||
|
||
res, err := createPullRequest(ctx, p.log, createPullRequestRequest{ | ||
Repository: repo, | ||
HeadBranch: input.Head, | ||
BaseBranch: input.Base, | ||
Title: input.Title, | ||
Description: input.Body, | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to create pull request for branch %q: %w", input.Head, err) | ||
} | ||
|
||
return &PullRequest{ | ||
Link: res.WebURL, | ||
}, nil | ||
} |
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,189 @@ | ||
//go:build integration | ||
// +build integration | ||
|
||
package git_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"testing" | ||
"time" | ||
|
||
"github.com/go-logr/logr" | ||
"github.com/google/go-github/v32/github" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/weaveworks/weave-gitops-enterprise/pkg/git" | ||
"golang.org/x/oauth2" | ||
) | ||
|
||
const ( | ||
TestRepositoryNamePrefix = "wge-integration-test-repo" | ||
) | ||
|
||
func init() { | ||
rand.Seed(time.Now().UnixNano()) | ||
} | ||
|
||
func TestCreatePullRequestInGitHubOrganization(t *testing.T) { | ||
// Create a client | ||
ctx := context.Background() | ||
client := github.NewClient( | ||
oauth2.NewClient(ctx, | ||
oauth2.StaticTokenSource( | ||
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}, | ||
), | ||
), | ||
) | ||
|
||
// Create a repository using a name that doesn't exist already | ||
repoName := fmt.Sprintf("%s-%03d", TestRepositoryNamePrefix, rand.Intn(1000)) | ||
repos, _, err := client.Repositories.ListByOrg(ctx, os.Getenv("GITHUB_ORG"), nil) | ||
assert.NoError(t, err) | ||
for findGitHubRepo(repos, repoName) != nil { | ||
repoName = fmt.Sprintf("%s-%03d", TestRepositoryNamePrefix, rand.Intn(1000)) | ||
} | ||
repo, _, err := client.Repositories.Create(ctx, os.Getenv("GITHUB_ORG"), &github.Repository{ | ||
Name: github.String(repoName), | ||
Private: github.Bool(true), | ||
AutoInit: github.Bool(true), | ||
}) | ||
require.NoError(t, err) | ||
defer func() { | ||
_, err = client.Repositories.Delete(ctx, os.Getenv("GITHUB_ORG"), repo.GetName()) | ||
require.NoError(t, err) | ||
}() | ||
|
||
p, err := git.NewFactory(logr.Discard()).Create(git.GitHubProviderName) | ||
require.NoError(t, err) | ||
content := "---\n" | ||
res, err := p.CreatePullRequest(ctx, git.PullRequestInput{ | ||
GitProvider: git.GitProvider{ | ||
Token: os.Getenv("GITHUB_TOKEN"), | ||
Type: git.GitHubProviderName, | ||
Hostname: "github.com", | ||
}, | ||
RepositoryURL: repo.GetCloneURL(), | ||
Head: "feature-01", | ||
Base: repo.GetDefaultBranch(), | ||
Title: "New cluster", | ||
Body: "Creates a cluster through a CAPI template", | ||
CommitMessage: "Add cluster manifest", | ||
Files: []git.CommitFile{ | ||
{ | ||
Path: "management/cluster-01.yaml", | ||
Content: &content, | ||
}, | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
pr, _, err := client.PullRequests.Get(ctx, os.Getenv("GITHUB_ORG"), repo.GetName(), 1) // #PR is 1 because it is a new repo | ||
require.NoError(t, err) | ||
assert.Equal(t, pr.GetHTMLURL(), res.Link) | ||
assert.Equal(t, pr.GetTitle(), "New cluster") | ||
assert.Equal(t, pr.GetBody(), "Creates a cluster through a CAPI template") | ||
assert.Equal(t, pr.GetChangedFiles(), 1) | ||
} | ||
|
||
func TestCreatePullRequestInGitHubUser(t *testing.T) { | ||
// Create a client | ||
ctx := context.Background() | ||
client := github.NewClient( | ||
oauth2.NewClient(ctx, | ||
oauth2.StaticTokenSource( | ||
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}, | ||
), | ||
), | ||
) | ||
// Create a repository using a name that doesn't exist already | ||
repoName := fmt.Sprintf("%s-%03d", TestRepositoryNamePrefix, rand.Intn(1000)) | ||
repos, _, err := client.Repositories.List(ctx, os.Getenv("GITHUB_USER"), nil) | ||
assert.NoError(t, err) | ||
for findGitHubRepo(repos, repoName) != nil { | ||
repoName = fmt.Sprintf("%s-%03d", TestRepositoryNamePrefix, rand.Intn(1000)) | ||
} | ||
repo, _, err := client.Repositories.Create(ctx, "", &github.Repository{ | ||
Name: github.String(repoName), | ||
Private: github.Bool(true), | ||
AutoInit: github.Bool(true), | ||
}) | ||
require.NoError(t, err) | ||
defer func() { | ||
_, err = client.Repositories.Delete(ctx, os.Getenv("GITHUB_USER"), repo.GetName()) | ||
require.NoError(t, err) | ||
}() | ||
|
||
p, err := git.NewFactory(logr.Discard()).Create(git.GitHubProviderName) | ||
require.NoError(t, err) | ||
content := "---\n" | ||
res, err := p.CreatePullRequest(ctx, git.PullRequestInput{ | ||
GitProvider: git.GitProvider{ | ||
Token: os.Getenv("GITHUB_TOKEN"), | ||
Type: git.GitHubProviderName, | ||
Hostname: "github.com", | ||
}, | ||
RepositoryURL: repo.GetCloneURL(), | ||
Head: "feature-01", | ||
Base: repo.GetDefaultBranch(), | ||
Title: "New cluster", | ||
Body: "Creates a cluster through a CAPI template", | ||
CommitMessage: "Add cluster manifest", | ||
Files: []git.CommitFile{ | ||
{ | ||
Path: "management/cluster-01.yaml", | ||
Content: &content, | ||
}, | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
pr, _, err := client.PullRequests.Get(ctx, os.Getenv("GITHUB_USER"), repo.GetName(), 1) // #PR is 1 because it is a new repo | ||
require.NoError(t, err) | ||
assert.Equal(t, pr.GetHTMLURL(), res.Link) | ||
assert.Equal(t, pr.GetTitle(), "New cluster") | ||
assert.Equal(t, pr.GetBody(), "Creates a cluster through a CAPI template") | ||
assert.Equal(t, pr.GetAdditions(), 1) | ||
|
||
res, err = p.CreatePullRequest(ctx, git.PullRequestInput{ | ||
GitProvider: git.GitProvider{ | ||
Token: os.Getenv("GITHUB_TOKEN"), | ||
Type: git.GitHubProviderName, | ||
Hostname: "github.com", | ||
}, | ||
RepositoryURL: repo.GetCloneURL(), | ||
Head: "feature-02", | ||
Base: "feature-01", | ||
Title: "Delete cluster", | ||
Body: "Deletes a cluster via gitops", | ||
CommitMessage: "Remove cluster manifest", | ||
Files: []git.CommitFile{ | ||
{ | ||
Path: "management/cluster-01.yaml", | ||
Content: nil, | ||
}, | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
pr, _, err = client.PullRequests.Get(ctx, os.Getenv("GITHUB_USER"), repo.GetName(), 2) // #PR is 2 because it is the 2nd PR for a new repo | ||
require.NoError(t, err) | ||
assert.Equal(t, pr.GetHTMLURL(), res.Link) | ||
assert.Equal(t, pr.GetTitle(), "Delete cluster") | ||
assert.Equal(t, pr.GetBody(), "Deletes a cluster via gitops") | ||
assert.Equal(t, pr.GetDeletions(), 1) | ||
} | ||
|
||
func findGitHubRepo(repos []*github.Repository, name string) *github.Repository { | ||
if name == "" { | ||
return nil | ||
} | ||
for _, repo := range repos { | ||
if repo.GetName() == name { | ||
return repo | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.