github.com/KusionStack/kpm@v0.8.4-0.20240326033734-dc72298a30e5/pkg/git/git.go (about)

     1  package git
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  
     7  	"github.com/go-git/go-git/v5"
     8  	"github.com/go-git/go-git/v5/plumbing"
     9  	"github.com/hashicorp/go-getter"
    10  )
    11  
    12  // CloneOptions is a struct for specifying options for cloning a git repository
    13  type CloneOptions struct {
    14  	RepoURL   string
    15  	Commit    string
    16  	Tag       string
    17  	Branch    string
    18  	LocalPath string
    19  	Writer    io.Writer
    20  }
    21  
    22  // CloneOption is a function that modifies CloneOptions
    23  type CloneOption func(*CloneOptions)
    24  
    25  func NewCloneOptions(repoUrl, commit, tag, branch, localpath string, Writer io.Writer) *CloneOptions {
    26  	return &CloneOptions{
    27  		RepoURL:   repoUrl,
    28  		Commit:    commit,
    29  		Tag:       tag,
    30  		Branch:    branch,
    31  		LocalPath: localpath,
    32  		Writer:    Writer,
    33  	}
    34  }
    35  
    36  // WithRepoURL sets the repo URL for CloneOptions
    37  func WithRepoURL(repoURL string) CloneOption {
    38  	return func(o *CloneOptions) {
    39  		o.RepoURL = repoURL
    40  	}
    41  }
    42  
    43  // WithBranch sets the branch for CloneOptions
    44  func WithBranch(branch string) CloneOption {
    45  	return func(o *CloneOptions) {
    46  		o.Branch = branch
    47  	}
    48  }
    49  
    50  // WithCommit sets the commit for CloneOptions
    51  func WithCommit(commit string) CloneOption {
    52  	return func(o *CloneOptions) {
    53  		o.Commit = commit
    54  	}
    55  }
    56  
    57  // WithTag sets the tag for CloneOptions
    58  func WithTag(tag string) CloneOption {
    59  	return func(o *CloneOptions) {
    60  		o.Tag = tag
    61  	}
    62  }
    63  
    64  // WithLocalPath sets the local path for CloneOptions
    65  func WithLocalPath(localPath string) CloneOption {
    66  	return func(o *CloneOptions) {
    67  		o.LocalPath = localPath
    68  	}
    69  }
    70  
    71  // WithWriter sets the writer for CloneOptions
    72  func WithWriter(writer io.Writer) CloneOption {
    73  	return func(o *CloneOptions) {
    74  		o.Writer = writer
    75  	}
    76  }
    77  
    78  // Validate checks if the CloneOptions are valid
    79  func (cloneOpts *CloneOptions) Validate() error {
    80  	onlyOneAllowed := 0
    81  	if cloneOpts.Branch != "" {
    82  		onlyOneAllowed++
    83  	}
    84  	if cloneOpts.Tag != "" {
    85  		onlyOneAllowed++
    86  	}
    87  	if cloneOpts.Commit != "" {
    88  		onlyOneAllowed++
    89  	}
    90  
    91  	if onlyOneAllowed > 1 {
    92  		return errors.New("only one of branch, tag or commit is allowed")
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // Clone clones a git repository
    99  func (cloneOpts *CloneOptions) Clone() (*git.Repository, error) {
   100  	if err := cloneOpts.Validate(); err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	url, err := cloneOpts.ForceGitUrl()
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	client := &getter.Client{
   110  		Src:       url,
   111  		Dst:       cloneOpts.LocalPath,
   112  		Pwd:       cloneOpts.LocalPath,
   113  		Mode:      getter.ClientModeDir,
   114  		Detectors: goGetterNoDetectors,
   115  		Getters:   goGetterGetters,
   116  	}
   117  
   118  	if err := client.Get(); err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	repo, err := git.PlainOpen(cloneOpts.LocalPath)
   123  
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	return repo, nil
   129  }
   130  
   131  // CloneWithOpts will clone from `repoURL` to `localPath` via git by using CloneOptions
   132  func CloneWithOpts(opts ...CloneOption) (*git.Repository, error) {
   133  	cloneOpts := &CloneOptions{}
   134  	for _, opt := range opts {
   135  		opt(cloneOpts)
   136  	}
   137  
   138  	err := cloneOpts.Validate()
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	return cloneOpts.Clone()
   144  }
   145  
   146  // Clone will clone from `repoURL` to `localPath` via git by tag name.
   147  // Deprecated: This function will be removed in a future version. Use CloneWithOpts instead.
   148  func Clone(repoURL string, tagName string, localPath string, writer io.Writer) (*git.Repository, error) {
   149  	repo, err := git.PlainClone(localPath, false, &git.CloneOptions{
   150  		URL:           repoURL,
   151  		Progress:      writer,
   152  		ReferenceName: plumbing.ReferenceName(plumbing.NewTagReferenceName(tagName)),
   153  	})
   154  	return repo, err
   155  }