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 }