Skip to content
Snippets Groups Projects
Select Git revision
  • f6bfb190ac40e4e9b2a1cbc840fdd93a47b11f70
  • master default protected
  • v1.17.0
  • v1.16.0
  • v1.15.0
  • v1.14.0
  • v1.13.0
  • v1.12.1
  • v1.12.0
  • v1.11.0
  • v1.10.0
  • v1.9.0
  • v1.8.0
  • v1.7.0
  • v1.6.0
  • v1.5.1
  • v1.5.0
  • v1.4.1
  • v1.4.0
  • v1.3.0
  • v1.2.0
  • v1.1.1
22 results

gitlab.go

Blame
  • user avatar
    Pavel Pletenev authored and Christoph Witzko committed
    f6bfb190
    History
    gitlab.go 6.28 KiB
    package provider
    
    import (
    	"errors"
    	"fmt"
    	"os"
    	"regexp"
    	"strconv"
    	"strings"
    	"time"
    
    	"github.com/Masterminds/semver/v3"
    	GitProvider "github.com/go-semantic-release/provider-git/pkg/provider"
    	"github.com/go-semantic-release/semantic-release/v2/pkg/provider"
    	"github.com/go-semantic-release/semantic-release/v2/pkg/semrel"
    	"github.com/xanzy/go-gitlab"
    )
    
    var PVERSION = "dev"
    
    type GitLabRepository struct {
    	projectID       string
    	branch          string
    	stripVTagPrefix bool
    	client          *gitlab.Client
    	localRepo       *GitProvider.Repository
    	useJobToken     bool
    }
    
    //gocyclo:ignore
    func (repo *GitLabRepository) Init(config map[string]string) error {
    	gitlabBaseURL := config["gitlab_baseurl"]
    	if gitlabBaseURL == "" {
    		gitlabBaseURL = os.Getenv("CI_SERVER_URL")
    	}
    
    	repo.useJobToken = false
    	token := config["token"]
    	if token == "" {
    		token = os.Getenv("GITLAB_TOKEN")
    	}
    	ciJobToken := os.Getenv("CI_JOB_TOKEN")
    	if token == "" {
    		if ciJobToken == "" {
    			return errors.New("gitlab token missing")
    		}
    		token = ciJobToken
    	}
    	// use the same strategy as goreleaser
    	if token == ciJobToken {
    		repo.useJobToken = true
    		if os.Getenv("GIT_STRATEGY") == "none" {
    			return errors.New("can not use job token with sparse-checkout repository")
    		}
    		repo.localRepo = &GitProvider.Repository{}
    		err := repo.localRepo.Init(map[string]string{
    			"remote_name": "origin",
    			"git_path":    os.Getenv("CI_PROJECT_DIR"),
    			"log_order":   config["log_order"],
    		})
    		if err != nil {
    			return errors.New("failed to initialize local git repository: " + err.Error())
    		}
    	}
    
    	branch := config["gitlab_branch"]
    	if branch == "" {
    		branch = os.Getenv("CI_COMMIT_BRANCH")
    	}
    
    	projectID := config["gitlab_projectid"]
    	if projectID == "" {
    		projectID = os.Getenv("CI_PROJECT_ID")
    	}
    	if projectID == "" {
    		return fmt.Errorf("gitlab_projectid is required")
    	}
    
    	var err error
    	stripVTagPrefix := config["strip_v_tag_prefix"]
    	repo.stripVTagPrefix, err = strconv.ParseBool(stripVTagPrefix)
    
    	if stripVTagPrefix != "" && err != nil {
    		return fmt.Errorf("failed to set property strip_v_tag_prefix: %w", err)
    	}
    
    	repo.projectID = projectID
    	repo.branch = branch
    
    	gitlabClientOpts := []gitlab.ClientOptionFunc{}
    	if gitlabBaseURL != "" {
    		gitlabClientOpts = append(gitlabClientOpts, gitlab.WithBaseURL(gitlabBaseURL))
    	}
    
    	var client *gitlab.Client
    	if repo.useJobToken {
    		client, err = gitlab.NewJobClient(token, gitlabClientOpts...)
    	} else {
    		client, err = gitlab.NewClient(token, gitlabClientOpts...)
    	}
    
    	if err != nil {
    		return fmt.Errorf("failed to create client: %w", err)
    	}
    
    	repo.client = client
    	return nil
    }
    
    func (repo *GitLabRepository) GetInfo() (*provider.RepositoryInfo, error) {
    	if repo.projectID == os.Getenv("CI_PROJECT_ID") {
    		return &provider.RepositoryInfo{
    			Owner:         os.Getenv("CI_PROJECT_NAMESPACE"),
    			Repo:          os.Getenv("CI_PROJECT_NAME"),
    			DefaultBranch: os.Getenv("CI_DEFAULT_BRANCH"),
    			Private:       os.Getenv("CI_PROJECT_VISIBILITY") != "public",
    		}, nil
    	}
    
    	project, _, err := repo.client.Projects.GetProject(repo.projectID, nil)
    	if err != nil {
    		return nil, err
    	}
    	namespace, repoName, _ := strings.Cut(project.PathWithNamespace, "/")
    	return &provider.RepositoryInfo{
    		Owner:         namespace,
    		Repo:          repoName,
    		DefaultBranch: project.DefaultBranch,
    		Private:       project.Visibility == gitlab.PrivateVisibility,
    	}, nil
    }
    
    func (repo *GitLabRepository) GetCommits(fromSha, toSha string) ([]*semrel.RawCommit, error) {
    	if repo.useJobToken {
    		return repo.localRepo.GetCommits(fromSha, toSha)
    	}
    
    	var refName *string
    	if fromSha == "" {
    		refName = gitlab.Ptr(toSha)
    	} else {
    		// No Matter the order ofr fromSha and toSha gitlab always returns commits in reverse chronological order
    		refName = gitlab.Ptr(fmt.Sprintf("%s...%s", fromSha, toSha))
    	}
    
    	opts := &gitlab.ListCommitsOptions{
    		ListOptions: gitlab.ListOptions{
    			Page:    1,
    			PerPage: 100,
    		},
    		RefName: refName,
    	}
    
    	allCommits := make([]*semrel.RawCommit, 0)
    
    	for {
    		commits, resp, err := repo.client.Commits.ListCommits(repo.projectID, opts)
    		if err != nil {
    			return nil, err
    		}
    
    		for _, commit := range commits {
    			allCommits = append(allCommits, &semrel.RawCommit{
    				SHA:        commit.ID,
    				RawMessage: commit.Message,
    				Annotations: map[string]string{
    					"author_name":     commit.AuthorName,
    					"author_email":    commit.AuthorEmail,
    					"author_date":     commit.AuthoredDate.Format(time.RFC3339),
    					"committer_name":  commit.CommitterName,
    					"committer_email": commit.CommitterEmail,
    					"committer_date":  commit.CommittedDate.Format(time.RFC3339),
    				},
    			})
    		}
    
    		// We cannot always rely on the total pages header
    		// https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23931
    		// if resp.CurrentPage >= resp.TotalPages {
    		if resp.NextPage == 0 {
    			break
    		}
    
    		opts.Page = resp.NextPage
    	}
    
    	return allCommits, nil
    }
    
    func (repo *GitLabRepository) GetReleases(rawRe string) ([]*semrel.Release, error) {
    	if repo.useJobToken {
    		return repo.localRepo.GetReleases(rawRe)
    	}
    
    	re := regexp.MustCompile(rawRe)
    	allReleases := make([]*semrel.Release, 0)
    
    	opts := &gitlab.ListTagsOptions{
    		ListOptions: gitlab.ListOptions{
    			Page:    1,
    			PerPage: 100,
    		},
    	}
    
    	for {
    		tags, resp, err := repo.client.Tags.ListTags(repo.projectID, opts)
    		if err != nil {
    			return nil, err
    		}
    
    		for _, tag := range tags {
    			if rawRe != "" && !re.MatchString(tag.Name) {
    				continue
    			}
    
    			version, err := semver.NewVersion(tag.Name)
    			if err != nil {
    				continue
    			}
    
    			allReleases = append(allReleases, &semrel.Release{
    				SHA:     tag.Commit.ID,
    				Version: version.String(),
    			})
    		}
    
    		if resp.CurrentPage >= resp.TotalPages {
    			break
    		}
    
    		opts.Page = resp.NextPage
    	}
    
    	return allReleases, nil
    }
    
    func (repo *GitLabRepository) CreateRelease(release *provider.CreateReleaseConfig) error {
    	prefix := "v"
    	if repo.stripVTagPrefix {
    		prefix = ""
    	}
    
    	tag := prefix + release.NewVersion
    
    	// Gitlab does not have any notion of pre-releases
    	_, _, err := repo.client.Releases.CreateRelease(repo.projectID, &gitlab.CreateReleaseOptions{
    		TagName: &tag,
    		Ref:     &release.SHA,
    		// TODO: this may been to be wrapped in ```
    		Description: &release.Changelog,
    	})
    
    	return err
    }
    
    func (repo *GitLabRepository) Name() string {
    	return "GitLab"
    }
    
    func (repo *GitLabRepository) Version() string {
    	return PVERSION
    }