github.com/jenkins-x/jx/v2@v2.1.155/pkg/collector/git_collector.go (about) 1 package collector 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 neturl "net/url" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/jenkins-x/jx-logging/pkg/log" 13 "github.com/jenkins-x/jx/v2/pkg/gits" 14 "github.com/jenkins-x/jx/v2/pkg/util" 15 "github.com/pkg/errors" 16 ) 17 18 // GitCollector stores the state for the git collector 19 type GitCollector struct { 20 gitInfo *gits.GitRepository 21 gitter gits.Gitter 22 gitBranch string 23 gitKind string 24 } 25 26 // NewGitCollector creates a new git based collector 27 func NewGitCollector(gitter gits.Gitter, gitURL string, gitBranch string, gitKind string) (Collector, error) { 28 gitInfo, err := gits.ParseGitURL(gitURL) 29 if err != nil { 30 return nil, err 31 } 32 33 return &GitCollector{ 34 gitter: gitter, 35 gitInfo: gitInfo, 36 gitBranch: gitBranch, 37 gitKind: gitKind, 38 }, nil 39 } 40 41 // CollectFiles collects files and returns the URLs 42 func (c *GitCollector) CollectFiles(patterns []string, outputPath string, basedir string) ([]string, error) { 43 urls := []string{} 44 45 gitClient := c.gitter 46 storageGitInfo := c.gitInfo 47 storageOrg := storageGitInfo.Organisation 48 storageRepoName := storageGitInfo.Name 49 50 ghPagesDir, err := cloneGitHubPagesBranchToTempDir(c.gitInfo.URL, gitClient, c.gitBranch) 51 if err != nil { 52 return urls, err 53 } 54 55 repoDir := filepath.Join(ghPagesDir, outputPath) 56 err = os.MkdirAll(repoDir, 0755) 57 if err != nil { 58 return urls, err 59 } 60 61 for _, p := range patterns { 62 fn := func(name string) error { 63 var err error 64 65 toName := name 66 if basedir != "" { 67 toName, err = filepath.Rel(basedir, name) 68 if err != nil { 69 return errors.Wrapf(err, "failed to remove basedir %s from %s", basedir, name) 70 } 71 } 72 toFile := filepath.Join(repoDir, toName) 73 toDir, _ := filepath.Split(toFile) 74 err = os.MkdirAll(toDir, util.DefaultWritePermissions) 75 if err != nil { 76 return errors.Wrapf(err, "failed to create directory file %s", toDir) 77 } 78 err = util.CopyFileOrDir(name, toFile, true) 79 if err != nil { 80 return errors.Wrapf(err, "failed to copy file %s to %s", name, toFile) 81 } 82 83 rPath := strings.TrimPrefix(strings.TrimPrefix(toFile, ghPagesDir), "/") 84 if rPath != "" { 85 url := c.generateURL(storageOrg, storageRepoName, rPath) 86 urls = append(urls, url) 87 } 88 return nil 89 } 90 91 err := util.GlobAllFiles("", p, fn) 92 if err != nil { 93 return urls, err 94 } 95 } 96 97 err = gitClient.Add(ghPagesDir, repoDir) 98 if err != nil { 99 return urls, err 100 } 101 changes, err := gitClient.HasChanges(ghPagesDir) 102 if err != nil { 103 return urls, err 104 } 105 if !changes { 106 return urls, nil 107 } 108 err = gitClient.CommitDir(ghPagesDir, fmt.Sprintf("Publishing files for path %s", outputPath)) 109 if err != nil { 110 log.Logger().Errorf("%s", err) 111 return urls, err 112 } 113 err = gitClient.Push(ghPagesDir, "origin", false, c.gitBranch) 114 return urls, err 115 } 116 117 // CollectData collects the data storing it at the given output path and returning the URL 118 // to access it 119 func (c *GitCollector) CollectData(data io.Reader, outputPath string) (string, error) { 120 u := "" 121 gitClient := c.gitter 122 storageGitInfo := c.gitInfo 123 storageOrg := storageGitInfo.Organisation 124 storageRepoName := storageGitInfo.Name 125 126 ghPagesDir, err := cloneGitHubPagesBranchToTempDir(c.gitInfo.URL, gitClient, c.gitBranch) 127 if err != nil { 128 return u, err 129 } 130 131 defer os.RemoveAll(ghPagesDir) 132 133 toFile := filepath.Join(ghPagesDir, outputPath) 134 toDir, _ := filepath.Split(toFile) 135 err = os.MkdirAll(toDir, util.DefaultWritePermissions) 136 if err != nil { 137 return u, errors.Wrapf(err, "failed to create directory file %s", toDir) 138 } 139 err = writeFile(toFile, data) 140 if err != nil { 141 return u, errors.Wrapf(err, "write file %s", toFile) 142 } 143 144 u = c.generateURL(storageOrg, storageRepoName, outputPath) 145 146 err = gitClient.Add(ghPagesDir, toDir) 147 if err != nil { 148 return u, err 149 } 150 changes, err := gitClient.HasChanges(ghPagesDir) 151 if err != nil { 152 return u, err 153 } 154 if !changes { 155 return u, nil 156 } 157 err = gitClient.CommitDir(ghPagesDir, fmt.Sprintf("Publishing files for path %s", outputPath)) 158 if err != nil { 159 return u, err 160 } 161 err = gitClient.Push(ghPagesDir, "origin", false, "HEAD:"+c.gitBranch) 162 return u, err 163 } 164 165 func writeFile(filePath string, data io.Reader) (err error) { 166 dest, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, util.DefaultWritePermissions) 167 if err != nil { 168 return 169 } 170 defer func() { 171 if e := dest.Close(); e != nil && err == nil { 172 err = e 173 } 174 }() 175 _, err = io.Copy(dest, data) 176 return 177 } 178 179 func (c *GitCollector) generateURL(storageOrg string, storageRepoName string, rPath string) (url string) { 180 if !c.gitInfo.IsGitHub() && c.gitKind == gits.KindGitHub { 181 url = fmt.Sprintf("https://%s/raw/%s/%s/%s/%s", c.gitInfo.Host, storageOrg, storageRepoName, c.gitBranch, rPath) 182 } else { 183 switch c.gitKind { 184 case gits.KindGitlab: 185 url = fmt.Sprintf("https://%s/api/v4/projects/%s/repository/files/%s/raw?ref=%s", 186 c.gitInfo.Host, 187 neturl.PathEscape(fmt.Sprintf("%s/%s", storageOrg, storageRepoName)), 188 neturl.PathEscape(rPath), 189 neturl.QueryEscape(c.gitBranch)) 190 case gits.KindBitBucketServer: 191 url = fmt.Sprintf("https://%s/projects/%s/repos/%s/raw/%s?at=%s", c.gitInfo.Host, storageOrg, storageRepoName, rPath, neturl.QueryEscape(fmt.Sprintf("refs/heads/%s", c.gitBranch))) 192 default: 193 // Fall back on GitHub for now 194 url = fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", storageOrg, storageRepoName, c.gitBranch, rPath) 195 } 196 } 197 log.Logger().Infof("Publishing %s", util.ColorInfo(url)) 198 return url 199 } 200 201 // cloneGitHubPagesBranchToTempDir clones the github pages branch to a temp dir 202 func cloneGitHubPagesBranchToTempDir(sourceURL string, gitClient gits.Gitter, branchName string) (string, error) { 203 // First clone the git repo 204 ghPagesDir, err := ioutil.TempDir("", "jenkins-x-collect") 205 if err != nil { 206 return ghPagesDir, err 207 } 208 209 log.Logger().Infof("shallow cloning %s branch %s", sourceURL, branchName) 210 err = gitClient.ShallowCloneBranch(sourceURL, branchName, ghPagesDir) 211 if err != nil { 212 log.Logger().Warnf("failed to shallow clone %s branch %s due to: %s", sourceURL, branchName, err.Error()) 213 214 os.RemoveAll(ghPagesDir) 215 ghPagesDir, err = ioutil.TempDir("", "jenkins-x-collect") 216 if err != nil { 217 return ghPagesDir, err 218 } 219 log.Logger().Infof("error doing shallow clone of branch %s: %v", branchName, err) 220 // swallow the error 221 log.Logger().Infof("No existing %s branch so creating it", branchName) 222 // branch doesn't exist, so we create it following the process on https://help.github.com/articles/creating-project-pages-using-the-command-line/ 223 err = gitClient.Clone(sourceURL, ghPagesDir) 224 if err != nil { 225 return ghPagesDir, err 226 } 227 err = gitClient.CheckoutOrphan(ghPagesDir, branchName) 228 if err != nil { 229 return ghPagesDir, err 230 } 231 err = gitClient.RemoveForce(ghPagesDir, ".") 232 if err != nil { 233 return ghPagesDir, err 234 } 235 err = os.Remove(filepath.Join(ghPagesDir, ".gitignore")) 236 if err != nil { 237 // Swallow the error, doesn't matter 238 } 239 } 240 return ghPagesDir, nil 241 }