Skip to content

Commit

Permalink
fix: Define new git provider interface
Browse files Browse the repository at this point in the history
Signed-off-by: Balazs Nadasdi <balazs@weave.works>
  • Loading branch information
yiannistri authored and yitsushi committed Mar 13, 2023
1 parent 6461fee commit 5a61ac3
Show file tree
Hide file tree
Showing 9 changed files with 1,053 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ jobs:
make dependencies
cd cmd/clusters-service
go test -v ./... -tags=integration
cd ../../pkg/git
go test -v ./... -tags=integration
ui-unit-tests:
runs-on: ubuntu-latest
Expand Down
64 changes: 64 additions & 0 deletions pkg/git/bitbucket_server.go
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
}
34 changes: 34 additions & 0 deletions pkg/git/factory.go
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)
}
}
66 changes: 66 additions & 0 deletions pkg/git/github.go
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
}
189 changes: 189 additions & 0 deletions pkg/git/github_test.go
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
}
Loading

0 comments on commit 5a61ac3

Please sign in to comment.