Skip to content

Commit

Permalink
Add pull request support to SCM generator
Browse files Browse the repository at this point in the history
This commit adds pull request support to SCM generator so the generator
can create ArgoCD apps for PRs as well.

Fixes argoproj#466

Signed-off-by: Fardin Khanjani <[email protected]>
  • Loading branch information
fardin01 committed Jan 20, 2022
1 parent c8fb714 commit e024751
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 13 deletions.
2 changes: 2 additions & 0 deletions api/v1alpha1/applicationset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ type SCMProviderGeneratorGithub struct {
TokenRef *SecretRef `json:"tokenRef,omitempty"`
// Scan all branches instead of just the default branch.
AllBranches bool `json:"allBranches,omitempty"`
// Scan all pull requests
AllPullRequests bool `json:"allPullRequests,omitempty"`
}

// SCMProviderGeneratorGitlab defines a connection info specific to Gitlab.
Expand Down
Binary file added applicationset
Binary file not shown.
4 changes: 2 additions & 2 deletions pkg/generators/scm_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
if err != nil {
return nil, fmt.Errorf("error fetching Github token: %v", err)
}
provider, err = scm_provider.NewGithubProvider(ctx, providerConfig.Github.Organization, token, providerConfig.Github.API, providerConfig.Github.AllBranches)
provider, err = scm_provider.NewGithubProvider(ctx, providerConfig.Github.Organization, token, providerConfig.Github.API, providerConfig.Github.AllBranches, providerConfig.Github.AllPullRequests)
if err != nil {
return nil, fmt.Errorf("error initializing Github service: %v", err)
}
Expand Down Expand Up @@ -92,7 +92,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
"organization": repo.Organization,
"repository": repo.Repository,
"url": repo.URL,
"branch": repo.Branch,
"revision": repo.Revision,
"sha": repo.SHA,
"labels": strings.Join(repo.Labels, ","),
})
Expand Down
61 changes: 54 additions & 7 deletions pkg/services/scm_provider/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ import (
"context"
"fmt"
"os"
"strconv"

"github.com/google/go-github/v35/github"
"golang.org/x/oauth2"
)

type GithubProvider struct {
client *github.Client
organization string
allBranches bool
client *github.Client
organization string
allBranches bool
allPullRequests bool
}

var _ SCMProviderService = &GithubProvider{}

func NewGithubProvider(ctx context.Context, organization string, token string, url string, allBranches bool) (*GithubProvider, error) {
func NewGithubProvider(ctx context.Context, organization string, token string, url string, allBranches bool, allPullRequests bool) (*GithubProvider, error) {
var ts oauth2.TokenSource
// Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits.
if token == "" {
Expand All @@ -39,7 +41,8 @@ func NewGithubProvider(ctx context.Context, organization string, token string, u
return nil, err
}
}
return &GithubProvider{client: client, organization: organization, allBranches: allBranches}, nil

return &GithubProvider{client: client, organization: organization, allBranches: allBranches, allPullRequests: allPullRequests}, nil
}

func (g *GithubProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) {
Expand Down Expand Up @@ -74,11 +77,29 @@ func (g *GithubProvider) ListRepos(ctx context.Context, cloneProtocol string) ([
Organization: githubRepo.Owner.GetLogin(),
Repository: githubRepo.GetName(),
URL: url,
Branch: branch.GetName(),
Revision: branch.GetName(),
SHA: branch.GetCommit().GetSHA(),
Labels: githubRepo.Topics,
})
}

if g.allPullRequests {
pullRequests, err := g.listPullRequests(ctx, githubRepo)
if err != nil {
return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", githubRepo.Owner.GetLogin(), githubRepo.GetName(), err)
}

for _, pr := range pullRequests {
repos = append(repos, &Repository{
Organization: githubRepo.Owner.GetLogin(),
Repository: githubRepo.GetName(),
URL: url,
Revision: strconv.FormatInt(int64(pr.GetNumber()), 10), // PR number is an int
SHA: pr.GetHead().GetSHA(),
Labels: githubRepo.Topics,
})
}
}
}
if resp.NextPage == 0 {
break
Expand All @@ -90,7 +111,7 @@ func (g *GithubProvider) ListRepos(ctx context.Context, cloneProtocol string) ([

func (g *GithubProvider) RepoHasPath(ctx context.Context, repo *Repository, path string) (bool, error) {
_, _, resp, err := g.client.Repositories.GetContents(ctx, repo.Organization, repo.Repository, path, &github.RepositoryContentGetOptions{
Ref: repo.Branch,
Ref: repo.Revision,
})
// 404s are not an error here, just a normal false.
if resp != nil && resp.StatusCode == 404 {
Expand Down Expand Up @@ -132,3 +153,29 @@ func (g *GithubProvider) listBranches(ctx context.Context, repo *github.Reposito
}
return branches, nil
}

func (g *GithubProvider) listPullRequests(ctx context.Context, repo *github.Repository) ([]github.PullRequest, error) {
opt := &github.PullRequestListOptions{
ListOptions: github.ListOptions{PerPage: 100},
}

pullRequests := []github.PullRequest{}

for {
allPullRequests, resp, err := g.client.PullRequests.List(ctx, repo.Owner.GetLogin(), repo.GetName(), opt)
if err != nil {
return nil, err
}

for _, pr := range allPullRequests {
pullRequests = append(pullRequests, *pr)
}

if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}

return pullRequests, nil
}
4 changes: 2 additions & 2 deletions pkg/services/scm_provider/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([
Organization: gitlabRepo.Namespace.FullPath,
Repository: gitlabRepo.Path,
URL: url,
Branch: branch.Name,
Revision: branch.Name,
SHA: branch.Commit.ID,
Labels: gitlabRepo.TagList,
})
Expand All @@ -93,7 +93,7 @@ func (g *GitlabProvider) RepoHasPath(_ context.Context, repo *Repository, path s
}
_, resp, err := g.client.Repositories.ListTree(p.ID, &gitlab.ListTreeOptions{
Path: &path,
Ref: &repo.Branch,
Ref: &repo.Revision,
})
if err != nil {
return false, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/scm_provider/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Repository struct {
Organization string
Repository string
URL string
Branch string
Revision string // Would love suggestions on better alternatives to "Revision". Something that can represent both a branch and a PR
SHA string
Labels []string
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/scm_provider/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func matchFilter(ctx context.Context, provider SCMProviderService, repo *Reposit
return false, nil
}

if filter.BranchMatch != nil && !filter.BranchMatch.MatchString(repo.Branch) {
if filter.BranchMatch != nil && !filter.BranchMatch.MatchString(repo.Revision) {
return false, nil
}

Expand Down

0 comments on commit e024751

Please sign in to comment.