You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-scm/scm/driver/gitlab/repo.go

356 lines
10 KiB
Go

// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gitlab
import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
"time"
"git.awesome-for.me/liuzhiguo/go-scm/scm"
"git.awesome-for.me/liuzhiguo/go-scm/scm/driver/internal/null"
)
type repository struct {
ID int `json:"id"`
Path string `json:"path"`
PathNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Visibility string `json:"visibility"`
Archived bool `json:"archived"`
WebURL string `json:"web_url"`
SSHURL string `json:"ssh_url_to_repo"`
HTTPURL string `json:"http_url_to_repo"`
Namespace namespace `json:"namespace"`
Permissions permissions `json:"permissions"`
}
type namespace struct {
Name string `json:"name"`
Path string `json:"path"`
FullPath string `json:"full_path"`
}
type permissions struct {
ProjectAccess access `json:"project_access"`
GroupAccess access `json:"group_access"`
}
type access struct {
AccessLevel int `json:"access_level"`
NotificationLevel int `json:"notification_level"`
}
type hook struct {
ID int `json:"id"`
URL string `json:"url"`
ProjectID int `json:"project_id"`
PushEvents bool `json:"push_events"`
IssuesEvents bool `json:"issues_events"`
MergeRequestsEvents bool `json:"merge_requests_events"`
TagPushEvents bool `json:"tag_push_events"`
NoteEvents bool `json:"note_events"`
JobEvents bool `json:"job_events"`
PipelineEvents bool `json:"pipeline_events"`
WikiPageEvents bool `json:"wiki_page_events"`
EnableSslVerification bool `json:"enable_ssl_verification"`
CreatedAt time.Time `json:"created_at"`
}
type repositoryService struct {
client *wrapper
}
func (s *repositoryService) Find(ctx context.Context, repo string) (*scm.Repository, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s", encode(repo))
out := new(repository)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertRepository(out), res, err
}
func (s *repositoryService) FindHook(ctx context.Context, repo string, id string) (*scm.Hook, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s/hooks/%s", encode(repo), id)
out := new(hook)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertHook(out), res, err
}
func (s *repositoryService) FindPerms(ctx context.Context, repo string) (*scm.Perm, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s", encode(repo))
out := new(repository)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertRepository(out).Perm, res, err
}
func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects?%s", encodeMemberListOptions(opts))
out := []*repository{}
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertRepositoryList(out), res, err
}
func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
// We pass the repo searchTerm in the query params and gitlab filters repos based on this search term
path := fmt.Sprintf("api/v4/projects?%s", encodeRepoListOptions(opts))
out := []*repository{}
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertRepositoryList(out), res, err
}
func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s/hooks?%s", encode(repo), encodeListOptions(opts))
out := []*hook{}
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertHookList(out), res, err
}
func (s *repositoryService) ListStatus(ctx context.Context, repo, ref string, opts scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s/repository/commits/%s/statuses?%s", encode(repo), ref, encodeListOptions(opts))
out := []*status{}
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertStatusList(out), res, err
}
func (s *repositoryService) CreateHook(ctx context.Context, repo string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
params := url.Values{}
params.Set("url", input.Target)
if input.Secret != "" {
params.Set("token", input.Secret)
}
if input.SkipVerify {
params.Set("enable_ssl_verification", "false")
}
if input.Events.Branch {
// no-op
}
if input.Events.Issue {
params.Set("issues_events", "true")
}
if input.Events.IssueComment ||
input.Events.PullRequestComment {
params.Set("note_events", "true")
}
if input.Events.PullRequest {
params.Set("merge_requests_events", "true")
}
if input.Events.Push || input.Events.Branch {
params.Set("push_events", "true")
}
if input.Events.Tag {
params.Set("tag_push_events", "true")
}
path := fmt.Sprintf("api/v4/projects/%s/hooks?%s", encode(repo), params.Encode())
out := new(hook)
res, err := s.client.do(ctx, "POST", path, nil, out)
return convertHook(out), res, err
}
func (s *repositoryService) CreateStatus(ctx context.Context, repo, ref string, input *scm.StatusInput) (*scm.Status, *scm.Response, error) {
params := url.Values{}
params.Set("state", convertFromState(input.State))
params.Set("name", input.Label)
params.Set("target_url", input.Target)
path := fmt.Sprintf("api/v4/projects/%s/statuses/%s?%s", encode(repo), ref, params.Encode())
out := new(status)
res, err := s.client.do(ctx, "POST", path, nil, out)
return convertStatus(out), res, err
}
func (s *repositoryService) UpdateHook(ctx context.Context, repo string, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
return nil, nil, scm.ErrNotSupported
}
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
path := fmt.Sprintf("api/v4/projects/%s/hooks/%s", encode(repo), id)
return s.client.do(ctx, "DELETE", path, nil, nil)
}
// helper function to convert from the gogs repository list to
// the common repository structure.
func convertRepositoryList(from []*repository) []*scm.Repository {
to := []*scm.Repository{}
for _, v := range from {
to = append(to, convertRepository(v))
}
return to
}
// helper function to convert from the gogs repository structure
// to the common repository structure.
func convertRepository(from *repository) *scm.Repository {
to := &scm.Repository{
ID: strconv.Itoa(from.ID),
Namespace: from.Namespace.Path,
Name: from.Path,
Branch: from.DefaultBranch,
Archived: from.Archived,
Private: convertPrivate(from.Visibility),
Visibility: convertVisibility(from.Visibility),
Clone: from.HTTPURL,
CloneSSH: from.SSHURL,
Link: from.WebURL,
Perm: &scm.Perm{
Pull: true,
Push: canPush(from),
Admin: canAdmin(from),
},
}
if path := from.Namespace.FullPath; path != "" {
to.Namespace = path
}
if to.Namespace == "" {
if parts := strings.SplitN(from.PathNamespace, "/", 2); len(parts) == 2 {
to.Namespace = parts[1]
}
}
return to
}
func convertHookList(from []*hook) []*scm.Hook {
to := []*scm.Hook{}
for _, v := range from {
to = append(to, convertHook(v))
}
return to
}
func convertHook(from *hook) *scm.Hook {
return &scm.Hook{
ID: strconv.Itoa(from.ID),
Active: true,
Target: from.URL,
Events: convertEvents(from),
SkipVerify: !from.EnableSslVerification,
}
}
type status struct {
Name string `json:"name"`
Desc null.String `json:"description"`
Status string `json:"status"`
Sha string `json:"sha"`
Ref string `json:"ref"`
Target null.String `json:"target_url"`
Created time.Time `json:"created_at"`
Updated time.Time `json:"updated_at"`
}
func convertStatusList(from []*status) []*scm.Status {
to := []*scm.Status{}
for _, v := range from {
to = append(to, convertStatus(v))
}
return to
}
func convertStatus(from *status) *scm.Status {
return &scm.Status{
State: convertState(from.Status),
Label: from.Name,
Desc: from.Desc.String,
Target: from.Target.String,
}
}
func convertEvents(from *hook) []string {
var events []string
if from.IssuesEvents {
events = append(events, "issues")
}
if from.TagPushEvents {
events = append(events, "tag")
}
if from.PushEvents {
events = append(events, "push")
}
if from.NoteEvents {
events = append(events, "comment")
}
if from.MergeRequestsEvents {
events = append(events, "merge")
}
return events
}
func convertState(from string) scm.State {
switch from {
case "canceled":
return scm.StateCanceled
case "failed":
return scm.StateFailure
case "pending":
return scm.StatePending
case "running":
return scm.StateRunning
case "success":
return scm.StateSuccess
default:
return scm.StateUnknown
}
}
func convertFromState(from scm.State) string {
switch from {
case scm.StatePending:
return "pending"
case scm.StateRunning:
return "running"
case scm.StateSuccess:
return "success"
case scm.StateCanceled:
return "canceled"
default:
return "failed"
}
}
func convertPrivate(from string) bool {
switch from {
case "public", "":
return false
default:
return true
}
}
func convertVisibility(from string) scm.Visibility {
switch from {
case "public":
return scm.VisibilityPublic
case "private":
return scm.VisibilityPrivate
case "internal":
return scm.VisibilityInternal
default:
return scm.VisibilityUndefined
}
}
func canPush(proj *repository) bool {
switch {
case proj.Permissions.ProjectAccess.AccessLevel >= 30:
return true
case proj.Permissions.GroupAccess.AccessLevel >= 30:
return true
default:
return false
}
}
func canAdmin(proj *repository) bool {
switch {
case proj.Permissions.ProjectAccess.AccessLevel >= 40:
return true
case proj.Permissions.GroupAccess.AccessLevel >= 40:
return true
default:
return false
}
}