github.com/covergates/covergates@v0.2.2-0.20201009050117-42ef8a19fb95/modules/scm/git.go (about)

     1  package scm
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/url"
    11  	"time"
    12  
    13  	"github.com/covergates/covergates/core"
    14  	"github.com/drone/go-scm/scm"
    15  	"github.com/drone/go-scm/scm/driver/github"
    16  	log "github.com/sirupsen/logrus"
    17  )
    18  
    19  type gitService struct {
    20  	git       core.Git
    21  	scm       core.SCMProvider
    22  	scmClient *scm.Client
    23  }
    24  
    25  type giteaCommit struct {
    26  	Sha       string           `json:"sha"`
    27  	Commit    *giteaRepoCommit `json:"commit"`
    28  	Committer *giteaUser       `json:"committer"`
    29  }
    30  
    31  type githubCommit struct {
    32  	SHA    string `json:"sha"`
    33  	Commit struct {
    34  		Committer struct {
    35  			Name  string    `json:"name"`
    36  			Email string    `json:"email"`
    37  			Date  time.Time `json:"date"`
    38  		} `json:"committer"`
    39  		Message string `json:"message"`
    40  	} `json:"commit"`
    41  	Committer struct {
    42  		AvatarURL string `json:"avatar_url"`
    43  		Login     string `json:"login"`
    44  	} `json:"committer"`
    45  }
    46  
    47  type giteaRepoCommit struct {
    48  	Message   string           `json:"message"`
    49  	Committer *giteaCommitUser `json:"committer"`
    50  }
    51  
    52  type giteaCommitUser struct {
    53  	Name string `json:"name"`
    54  }
    55  
    56  type giteaUser struct {
    57  	UserName string `json:"username"`
    58  	Avatar   string `json:"avatar_url"`
    59  }
    60  
    61  func (service *gitService) FindCommit(ctx context.Context, user *core.User, repo *core.Repo) string {
    62  	client := service.scmClient
    63  	ctx = withUser(ctx, service.scm, user)
    64  	ref, _, err := client.Git.FindBranch(
    65  		ctx,
    66  		fmt.Sprintf("%s/%s", repo.NameSpace, repo.Name),
    67  		repo.Branch,
    68  	)
    69  	if err != nil {
    70  		return ""
    71  	}
    72  	return ref.Sha
    73  }
    74  
    75  func (service *gitService) ListCommits(ctx context.Context, user *core.User, repo string) ([]*core.Commit, error) {
    76  	ctx = withUser(ctx, service.scm, user)
    77  	if service.scm == core.Gitea {
    78  		return service.listGiteaCommits(ctx, repo, "")
    79  	}
    80  	return service.listCommits(ctx, repo, "")
    81  }
    82  
    83  func (service *gitService) ListCommitsByRef(ctx context.Context, user *core.User, repo, ref string) ([]*core.Commit, error) {
    84  	ctx = withUser(ctx, service.scm, user)
    85  	switch service.scm {
    86  	case core.Gitea:
    87  		return service.listGiteaCommits(ctx, repo, ref)
    88  	case core.Github:
    89  		return service.listGithubCommits(ctx, repo, ref)
    90  	default:
    91  		return service.listCommits(ctx, repo, ref)
    92  	}
    93  }
    94  
    95  func (service *gitService) listCommits(ctx context.Context, repo, ref string) ([]*core.Commit, error) {
    96  	client := service.scmClient
    97  	options := scm.CommitListOptions{Size: 20}
    98  	if ref != "" {
    99  		options.Ref = ref
   100  	}
   101  	commits, _, err := client.Git.ListCommits(ctx, repo, options)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	results := make([]*core.Commit, len(commits))
   106  	for i, commit := range commits {
   107  		results[i] = &core.Commit{
   108  			Sha:             commit.Sha,
   109  			Message:         commit.Message,
   110  			Committer:       commit.Committer.Name,
   111  			CommitterAvater: commit.Committer.Avatar,
   112  		}
   113  	}
   114  	return results, nil
   115  }
   116  
   117  func mustGetGiteaCommitsQuery(repo, ref string) string {
   118  	u, err := url.Parse(fmt.Sprintf("api/v1/repos/%s/commits", repo))
   119  	if err != nil {
   120  		log.Fatal(err)
   121  	}
   122  	query := u.Query()
   123  	if ref != "" {
   124  		query.Set("sha", ref)
   125  	}
   126  	u.RawQuery = query.Encode()
   127  	return u.String()
   128  }
   129  
   130  func (service *gitService) listGiteaCommits(ctx context.Context, repo, ref string) ([]*core.Commit, error) {
   131  	client := service.scmClient
   132  	res, err := client.Do(ctx, &scm.Request{
   133  		Header: map[string][]string{
   134  			"Content-Type": {"application/json"},
   135  		},
   136  		Method: "GET",
   137  		Path:   mustGetGiteaCommitsQuery(repo, ref),
   138  	})
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	defer res.Body.Close()
   143  	if res.Status > 300 {
   144  		return nil, errors.New(http.StatusText(res.Status))
   145  	}
   146  	data, err := ioutil.ReadAll(res.Body)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	var commits []giteaCommit
   151  	if err := json.Unmarshal(data, &commits); err != nil {
   152  		return nil, err
   153  	}
   154  	results := make([]*core.Commit, len(commits))
   155  	for i, commit := range commits {
   156  		results[i] = &core.Commit{
   157  			Sha:     commit.Sha,
   158  			Message: commit.Commit.Message,
   159  		}
   160  		if commit.Committer != nil {
   161  			results[i].Committer = commit.Committer.UserName
   162  			results[i].CommitterAvater = commit.Committer.Avatar
   163  		} else {
   164  			results[i].Committer = commit.Commit.Committer.Name
   165  		}
   166  	}
   167  	return results, nil
   168  }
   169  
   170  func (service *gitService) listGithubCommits(ctx context.Context, repo, ref string) ([]*core.Commit, error) {
   171  	params := url.Values{}
   172  	params.Add("sha", ref)
   173  	params.Add("per_page", "25")
   174  
   175  	res, err := service.scmClient.Do(ctx, &scm.Request{
   176  		Method: "GET",
   177  		Path:   fmt.Sprintf("repos/%s/commits?%s", repo, params.Encode()),
   178  	})
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	defer res.Body.Close()
   183  	if res.Status > 300 {
   184  		var err *github.Error
   185  		json.NewDecoder(res.Body).Decode(err)
   186  		return nil, err
   187  	}
   188  	var commits []*githubCommit
   189  	json.NewDecoder(res.Body).Decode(&commits)
   190  	result := make([]*core.Commit, len(commits))
   191  	for i, commit := range commits {
   192  		result[i] = &core.Commit{
   193  			Committer:       commit.Commit.Committer.Name,
   194  			CommitterAvater: commit.Committer.AvatarURL,
   195  			Message:         commit.Commit.Message,
   196  			Sha:             commit.SHA,
   197  		}
   198  	}
   199  	return result, nil
   200  }
   201  
   202  func (service *gitService) ListBranches(ctx context.Context, user *core.User, repo string) ([]string, error) {
   203  	client := service.scmClient
   204  	ctx = withUser(ctx, service.scm, user)
   205  	references, _, err := client.Git.ListBranches(ctx, repo, scm.ListOptions{})
   206  	if err != nil {
   207  		return []string{}, err
   208  	}
   209  	branches := make([]string, len(references))
   210  	for i, ref := range references {
   211  		branches[i] = ref.Name
   212  	}
   213  	return branches, nil
   214  }
   215  
   216  // GitRepository clone
   217  func (service *gitService) GitRepository(ctx context.Context, user *core.User, repo string) (core.GitRepository, error) {
   218  	client := service.scmClient
   219  	rs := &repoService{scm: service.scm, client: client}
   220  	token := userToken(service.scm, user)
   221  	ctx = withUser(ctx, service.scm, user)
   222  	url, err := rs.CloneURL(ctx, user, repo)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	return service.git.Clone(ctx, url, token.Token)
   227  }