github.com/jenkins-x/jx/v2@v2.1.155/pkg/versionstream/versionstreamrepo/gitrepo.go (about) 1 package versionstreamrepo 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1" 10 "github.com/jenkins-x/jx/v2/pkg/gits" 11 12 "github.com/jenkins-x/jx-logging/pkg/log" 13 "github.com/jenkins-x/jx/v2/pkg/config" 14 "github.com/jenkins-x/jx/v2/pkg/util" 15 "github.com/pkg/errors" 16 "gopkg.in/AlecAivazis/survey.v1" 17 "gopkg.in/src-d/go-git.v4" 18 "gopkg.in/src-d/go-git.v4/plumbing" 19 ) 20 21 // CloneJXVersionsRepo clones the jenkins-x versions repo to a local working dir 22 func CloneJXVersionsRepo(versionRepository string, versionRef string, settings *v1.TeamSettings, gitter gits.Gitter, batchMode bool, advancedMode bool, handles util.IOFileHandles) (string, string, error) { 23 dir, versionRef, err := cloneJXVersionsRepo(versionRepository, versionRef, settings, gitter, batchMode, advancedMode, handles) 24 if err != nil { 25 return "", "", errors.Wrapf(err, "") 26 } 27 if versionRef != "" { 28 resolved, err := resolveRefToTag(dir, versionRef, gitter) 29 if err != nil { 30 return "", "", errors.WithStack(err) 31 } 32 return dir, resolved, nil 33 } 34 return dir, "", nil 35 } 36 37 func cloneJXVersionsRepo(versionRepository string, versionRef string, settings *v1.TeamSettings, gitter gits.Gitter, batchMode bool, advancedMode bool, handles util.IOFileHandles) (string, string, error) { 38 surveyOpts := survey.WithStdio(handles.In, handles.Out, handles.Err) 39 configDir, err := util.ConfigDir() 40 if err != nil { 41 return "", "", fmt.Errorf("error determining config dir %v", err) 42 } 43 wrkDir := filepath.Join(configDir, "jenkins-x-versions") 44 45 if settings != nil { 46 if versionRepository == "" { 47 versionRepository = settings.VersionStreamURL 48 } 49 if versionRef == "" { 50 versionRef = settings.VersionStreamRef 51 } 52 } 53 if versionRepository == "" { 54 versionRepository = config.DefaultVersionsURL 55 } 56 if versionRef == "" { 57 versionRef = config.DefaultVersionsRef 58 } 59 60 log.Logger().Debugf("Current configuration dir: %s", configDir) 61 log.Logger().Debugf("VersionRepository: %s git ref: %s", versionRepository, versionRef) 62 63 // If the repo already exists let's try to fetch the latest version 64 if exists, err := util.DirExists(wrkDir); err == nil && exists { 65 pullLatest := false 66 if batchMode { 67 pullLatest = true 68 } else if advancedMode { 69 confirm := &survey.Confirm{ 70 Message: "A local Jenkins X versions repository already exists, pull the latest?", 71 Default: true, 72 } 73 err = survey.AskOne(confirm, &pullLatest, nil, surveyOpts) 74 if err != nil { 75 log.Logger().Errorf("Error confirming if we should pull latest, skipping %s", wrkDir) 76 } 77 } else { 78 pullLatest = true 79 log.Logger().Debugf(util.QuestionAnswer("A local Jenkins X versions repository already exists, pulling the latest", util.YesNo(pullLatest))) 80 } 81 if pullLatest { 82 err = gitter.FetchRemoteTags(wrkDir, versionRepository) 83 if err != nil { 84 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 85 if err != nil { 86 return "", "", errors.WithStack(err) 87 } 88 return dir, versionRef, nil 89 } 90 err = gitter.FetchBranch(wrkDir, versionRepository, versionRef) 91 if err != nil { 92 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 93 if err != nil { 94 return "", "", errors.WithStack(err) 95 } 96 return dir, versionRef, nil 97 } 98 99 isBranch, err := gits.RefIsBranch(wrkDir, versionRef, gitter) 100 if err != nil { 101 return "", "", err 102 } 103 104 if versionRef == config.DefaultVersionsRef || isBranch { 105 err = gitter.Checkout(wrkDir, versionRef) 106 if err != nil { 107 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 108 if err != nil { 109 return "", "", errors.WithStack(err) 110 } 111 return dir, versionRef, nil 112 } 113 err = gitter.Reset(wrkDir, "FETCH_HEAD", true) 114 if err != nil { 115 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 116 if err != nil { 117 return "", "", errors.WithStack(err) 118 } 119 return dir, versionRef, nil 120 } 121 } else { 122 err = gitter.Checkout(wrkDir, "FETCH_HEAD") 123 if err != nil { 124 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 125 if err != nil { 126 return "", "", errors.WithStack(err) 127 } 128 return dir, versionRef, nil 129 } 130 } 131 } 132 return wrkDir, versionRef, err 133 } 134 dir, err := deleteAndReClone(wrkDir, versionRepository, versionRef, gitter) 135 if err != nil { 136 return "", "", errors.WithStack(err) 137 } 138 return dir, versionRef, nil 139 } 140 141 func deleteAndReClone(wrkDir string, versionRepository string, referenceName string, gitter gits.Gitter) (string, error) { 142 log.Logger().Debug("Deleting and cloning the Jenkins X versions repo") 143 err := os.RemoveAll(wrkDir) 144 if err != nil { 145 return "", errors.Wrapf(err, "failed to delete dir %s: %s\n", wrkDir, err.Error()) 146 } 147 err = os.MkdirAll(wrkDir, util.DefaultWritePermissions) 148 if err != nil { 149 return "", errors.Wrapf(err, "failed to ensure directory is created %s", wrkDir) 150 } 151 _, err = clone(wrkDir, versionRepository, referenceName, gitter) 152 if err != nil { 153 return "", err 154 } 155 return wrkDir, err 156 } 157 158 func clone(wrkDir string, versionRepository string, referenceName string, gitter gits.Gitter) (string, error) { 159 if referenceName == "" || referenceName == "master" { 160 referenceName = "refs/heads/master" 161 } else if !strings.Contains(referenceName, "/") { 162 if strings.HasPrefix(referenceName, "PR-") { 163 prNumber := strings.TrimPrefix(referenceName, "PR-") 164 165 log.Logger().Debugf("Cloning the Jenkins X versions repo %s with PR: %s to %s", util.ColorInfo(versionRepository), util.ColorInfo(referenceName), util.ColorInfo(wrkDir)) 166 return "", shallowCloneGitRepositoryToDir(wrkDir, versionRepository, prNumber, "", gitter) 167 } 168 log.Logger().Debugf("Cloning the Jenkins X versions repo %s with revision %s to %s", util.ColorInfo(versionRepository), util.ColorInfo(referenceName), util.ColorInfo(wrkDir)) 169 170 err := gitter.Clone(versionRepository, wrkDir) 171 if err != nil { 172 return "", errors.Wrapf(err, "failed to clone repository: %s to dir %s", versionRepository, wrkDir) 173 } 174 cmd := util.Command{ 175 Dir: wrkDir, 176 Name: "git", 177 Args: []string{"fetch", "origin", referenceName}, 178 } 179 _, err = cmd.RunWithoutRetry() 180 if err != nil { 181 return "", errors.Wrapf(err, "failed to git fetch origin %s for repo: %s in dir %s", referenceName, versionRepository, wrkDir) 182 } 183 isBranch, err := gits.RefIsBranch(wrkDir, referenceName, gitter) 184 if err != nil { 185 return "", err 186 } 187 if isBranch { 188 err = gitter.Checkout(wrkDir, referenceName) 189 if err != nil { 190 return "", errors.Wrapf(err, "failed to checkout %s of repo: %s in dir %s", referenceName, versionRepository, wrkDir) 191 } 192 return "", nil 193 } 194 err = gitter.Checkout(wrkDir, "FETCH_HEAD") 195 if err != nil { 196 return "", errors.Wrapf(err, "failed to checkout FETCH_HEAD of repo: %s in dir %s", versionRepository, wrkDir) 197 } 198 return "", nil 199 } 200 log.Logger().Infof("Cloning the Jenkins X versions repo %s with ref %s to %s", util.ColorInfo(versionRepository), util.ColorInfo(referenceName), util.ColorInfo(wrkDir)) 201 // TODO: Change this to use gitter instead, but need to understand exactly what it's doing first. 202 _, err := git.PlainClone(wrkDir, false, &git.CloneOptions{ 203 URL: versionRepository, 204 ReferenceName: plumbing.ReferenceName(referenceName), 205 SingleBranch: true, 206 Progress: nil, 207 }) 208 if err != nil { 209 return "", errors.Wrapf(err, "failed to clone reference: %s", referenceName) 210 } 211 return "", err 212 } 213 214 func shallowCloneGitRepositoryToDir(dir string, gitURL string, pullRequestNumber string, revision string, gitter gits.Gitter) error { 215 if pullRequestNumber != "" { 216 log.Logger().Infof("shallow cloning pull request %s of repository %s to temp dir %s", gitURL, 217 pullRequestNumber, dir) 218 err := gitter.ShallowClone(dir, gitURL, "", pullRequestNumber) 219 if err != nil { 220 return errors.Wrapf(err, "shallow cloning pull request %s of repository %s to temp dir %s\n", gitURL, 221 pullRequestNumber, dir) 222 } 223 } else if revision != "" { 224 log.Logger().Infof("shallow cloning revision %s of repository %s to temp dir %s", gitURL, 225 revision, dir) 226 err := gitter.ShallowClone(dir, gitURL, revision, "") 227 if err != nil { 228 return errors.Wrapf(err, "shallow cloning revision %s of repository %s to temp dir %s\n", gitURL, 229 revision, dir) 230 } 231 } else { 232 log.Logger().Infof("shallow cloning master of repository %s to temp dir %s", gitURL, dir) 233 err := gitter.ShallowClone(dir, gitURL, "", "") 234 if err != nil { 235 return errors.Wrapf(err, "shallow cloning master of repository %s to temp dir %s\n", gitURL, dir) 236 } 237 } 238 239 return nil 240 } 241 242 func resolveRefToTag(dir string, commitish string, gitter gits.Gitter) (string, error) { 243 err := gitter.FetchTags(dir) 244 if err != nil { 245 return "", errors.Wrapf(err, "fetching tags") 246 } 247 resolved, _, err := gitter.Describe(dir, true, commitish, "0", true) 248 if err != nil { 249 return "", errors.Wrapf(err, "running git describe %s --abbrev=0", commitish) 250 } 251 if resolved != "" { 252 return resolved, nil 253 } 254 return resolved, nil 255 }