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  }