github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/services/scm_provider/bitbucket_cloud.go (about)

     1  package scm_provider
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  
     9  	bitbucket "github.com/ktrysmt/go-bitbucket"
    10  )
    11  
    12  type BitBucketCloudProvider struct {
    13  	client      *ExtendedClient
    14  	allBranches bool
    15  	owner       string
    16  }
    17  
    18  type ExtendedClient struct {
    19  	*bitbucket.Client
    20  	username string
    21  	password string
    22  	owner    string
    23  }
    24  
    25  func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error) {
    26  	urlStr := c.GetApiBaseURL()
    27  
    28  	// Getting file contents from V2 defined at https://developer.atlassian.com/cloud/bitbucket/rest/api-group-source/#api-repositories-workspace-repo-slug-src-commit-path-get
    29  	urlStr += fmt.Sprintf("/repositories/%s/%s/src/%s/%s?format=meta", c.owner, repo.Repository, repo.SHA, path)
    30  	body := strings.NewReader("")
    31  
    32  	req, err := http.NewRequest(http.MethodGet, urlStr, body)
    33  	if err != nil {
    34  		return false, err
    35  	}
    36  	req.SetBasicAuth(c.username, c.password)
    37  	resp, err := c.HttpClient.Do(req)
    38  	if err != nil {
    39  		return false, err
    40  	}
    41  	defer resp.Body.Close()
    42  	if resp.StatusCode == http.StatusNotFound {
    43  		return false, nil
    44  	}
    45  	if resp.StatusCode == http.StatusOK {
    46  		return true, nil
    47  	}
    48  
    49  	return false, fmt.Errorf(resp.Status)
    50  }
    51  
    52  var _ SCMProviderService = &BitBucketCloudProvider{}
    53  
    54  func NewBitBucketCloudProvider(ctx context.Context, owner string, user string, password string, allBranches bool) (*BitBucketCloudProvider, error) {
    55  
    56  	client := &ExtendedClient{
    57  		bitbucket.NewBasicAuth(user, password),
    58  		user,
    59  		password,
    60  		owner,
    61  	}
    62  	return &BitBucketCloudProvider{client: client, owner: owner, allBranches: allBranches}, nil
    63  }
    64  
    65  func (g *BitBucketCloudProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
    66  	repos := []*Repository{}
    67  	branches, err := g.listBranches(repo)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("error listing branches for %s/%s: %v", repo.Organization, repo.Repository, err)
    70  	}
    71  
    72  	for _, branch := range branches {
    73  		hash, ok := branch.Target["hash"].(string)
    74  		if !ok {
    75  			return nil, fmt.Errorf("error getting SHA for branch for %s/%s/%s: %v", g.owner, repo.Repository, branch.Name, err)
    76  		}
    77  		repos = append(repos, &Repository{
    78  			Organization: repo.Organization,
    79  			Repository:   repo.Repository,
    80  			URL:          repo.URL,
    81  			Branch:       branch.Name,
    82  			SHA:          hash,
    83  			Labels:       repo.Labels,
    84  			RepositoryId: repo.RepositoryId,
    85  		})
    86  	}
    87  	return repos, nil
    88  }
    89  
    90  func (g *BitBucketCloudProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) {
    91  	if cloneProtocol == "" {
    92  		cloneProtocol = "ssh"
    93  	}
    94  	opt := &bitbucket.RepositoriesOptions{
    95  		Owner: g.owner,
    96  		Role:  "member",
    97  	}
    98  	repos := []*Repository{}
    99  	accountReposResp, err := g.client.Repositories.ListForAccount(opt)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("error listing repositories for %s: %v", g.owner, err)
   102  	}
   103  	for _, bitBucketRepo := range accountReposResp.Items {
   104  		cloneUrl, err := findCloneURL(cloneProtocol, &bitBucketRepo)
   105  		if err != nil {
   106  			return nil, fmt.Errorf("error fetching clone url for repo %s: %v", bitBucketRepo.Slug, err)
   107  		}
   108  		repos = append(repos, &Repository{
   109  			Organization: g.owner,
   110  			Repository:   bitBucketRepo.Slug,
   111  			Branch:       bitBucketRepo.Mainbranch.Name,
   112  			URL:          *cloneUrl,
   113  			Labels:       []string{},
   114  			RepositoryId: bitBucketRepo.Uuid,
   115  		})
   116  	}
   117  	return repos, nil
   118  }
   119  
   120  func (g *BitBucketCloudProvider) RepoHasPath(ctx context.Context, repo *Repository, path string) (bool, error) {
   121  	contents, err := g.client.GetContents(repo, path)
   122  	if err != nil {
   123  		return false, err
   124  	}
   125  	if contents {
   126  		return true, nil
   127  	}
   128  	return false, nil
   129  }
   130  
   131  func (g *BitBucketCloudProvider) listBranches(repo *Repository) ([]bitbucket.RepositoryBranch, error) {
   132  	if !g.allBranches {
   133  		repoBranch, err := g.client.Repositories.Repository.GetBranch(&bitbucket.RepositoryBranchOptions{
   134  			Owner:      g.owner,
   135  			RepoSlug:   repo.Repository,
   136  			BranchName: repo.Branch,
   137  		})
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  		return []bitbucket.RepositoryBranch{
   142  			*repoBranch,
   143  		}, nil
   144  	}
   145  
   146  	branches, err := g.client.Repositories.Repository.ListBranches(&bitbucket.RepositoryBranchOptions{
   147  		Owner:    g.owner,
   148  		RepoSlug: repo.Repository,
   149  	})
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	return branches.Branches, nil
   154  
   155  }
   156  
   157  func findCloneURL(cloneProtocol string, repo *bitbucket.Repository) (*string, error) {
   158  
   159  	cloneLinks, ok := repo.Links["clone"].([]interface{})
   160  	if !ok {
   161  		return nil, fmt.Errorf("unknown type returned from repo links")
   162  	}
   163  	for _, link := range cloneLinks {
   164  		linkEntry, ok := link.(map[string]interface{})
   165  		if !ok {
   166  			return nil, fmt.Errorf("unknown type returned from clone link")
   167  		}
   168  		if linkEntry["name"] == cloneProtocol {
   169  			url, ok := linkEntry["href"].(string)
   170  			if !ok {
   171  				return nil, fmt.Errorf("could not find href for clone link")
   172  			}
   173  			return &url, nil
   174  		}
   175  	}
   176  	return nil, fmt.Errorf("unknown clone protocol for Bitbucket cloud %v", cloneProtocol)
   177  }