...

Source file src/github.com/redhat-developer/odo/pkg/registry/starter_project.go

Documentation: github.com/redhat-developer/odo/pkg/registry

     1  package registry
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
    10  
    11  	devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
    12  	parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
    13  	"github.com/go-git/go-git/v5"
    14  	"github.com/go-git/go-git/v5/plumbing"
    15  	"github.com/go-git/go-git/v5/plumbing/transport/http"
    16  
    17  	"github.com/redhat-developer/odo/pkg/devfile/location"
    18  	"github.com/redhat-developer/odo/pkg/log"
    19  	"github.com/redhat-developer/odo/pkg/util"
    20  )
    21  
    22  const (
    23  	RegistryUser = "default"
    24  )
    25  
    26  func checkoutProject(subDir, zipURL, path, starterToken string, fsys filesystem.Filesystem) error {
    27  
    28  	if subDir == "" {
    29  		subDir = "/"
    30  	}
    31  	err := util.GetAndExtractZip(zipURL, path, subDir, starterToken, fsys)
    32  	if err != nil {
    33  		return fmt.Errorf("failed to download and extract project zip folder: %w", err)
    34  	}
    35  	return nil
    36  }
    37  
    38  // DownloadStarterProject downloads a starter project referenced in devfile
    39  // This will first remove the content of the contextDir
    40  func DownloadStarterProject(fs filesystem.Filesystem, starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error {
    41  	var path string
    42  	var err error
    43  	// Retrieve the working directory in order to clone correctly
    44  	if contextDir == "" {
    45  		path, err = os.Getwd()
    46  		if err != nil {
    47  			return fmt.Errorf("could not get the current working directory: %w", err)
    48  		}
    49  	} else {
    50  		path = contextDir
    51  	}
    52  
    53  	// We will check to see if the project has a valid directory
    54  	err = util.IsValidProjectDir(path, location.DevfileLocation(fs, ""), fs)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	if verbose {
    60  		log.Info("\nStarter Project")
    61  	}
    62  
    63  	if starterProject.Git != nil {
    64  		err := downloadGitProject(starterProject, decryptedToken, path, verbose)
    65  
    66  		if err != nil {
    67  			return err
    68  		}
    69  
    70  	} else if starterProject.Zip != nil {
    71  		url := starterProject.Zip.Location
    72  		sparseDir := starterProject.SubDir
    73  		var downloadSpinner *log.Status
    74  		if verbose {
    75  			downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, url)
    76  		}
    77  		err := checkoutProject(sparseDir, url, path, decryptedToken, fs)
    78  		if err != nil {
    79  			if verbose {
    80  				downloadSpinner.End(false)
    81  			}
    82  			return err
    83  		}
    84  		if verbose {
    85  			downloadSpinner.End(true)
    86  		}
    87  	} else {
    88  		return errors.New("project type not supported")
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  // downloadGitProject downloads the git starter projects from devfile.yaml
    95  func downloadGitProject(starterProject *devfilev1.StarterProject, starterToken, path string, verbose bool) error {
    96  	remoteName, remoteUrl, revision, err := parsercommon.GetDefaultSource(starterProject.Git.GitLikeProjectSource)
    97  	if err != nil {
    98  		return fmt.Errorf("unable to get default project source for starter project %s: %w", starterProject.Name, err)
    99  	}
   100  
   101  	// convert revision to referenceName type, ref name could be a branch or tag
   102  	// if revision is not specified it would be the default branch of the project
   103  	refName := plumbing.ReferenceName(revision)
   104  
   105  	if plumbing.IsHash(revision) {
   106  		// Specifying commit in the reference name is not supported by the go-git library
   107  		// while doing git.PlainClone()
   108  		log.Warning("Specifying commit in 'revision' is not yet supported in odo.")
   109  		// overriding revision to empty as we do not support this
   110  		revision = ""
   111  	}
   112  
   113  	if revision != "" {
   114  		// lets consider revision to be a branch name first
   115  		refName = plumbing.NewBranchReferenceName(revision)
   116  	}
   117  
   118  	var downloadSpinner *log.Status
   119  	if verbose {
   120  		downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, remoteUrl)
   121  		defer downloadSpinner.End(false)
   122  	}
   123  
   124  	cloneOptions := &git.CloneOptions{
   125  		URL:        remoteUrl,
   126  		RemoteName: remoteName,
   127  		// we don't need history for starter projects
   128  		Depth: 1,
   129  	}
   130  
   131  	if refName != "" {
   132  		cloneOptions.ReferenceName = refName
   133  		cloneOptions.SingleBranch = true
   134  
   135  	}
   136  
   137  	if starterToken != "" {
   138  		cloneOptions.Auth = &http.BasicAuth{
   139  			Username: RegistryUser,
   140  			Password: starterToken,
   141  		}
   142  	}
   143  
   144  	originalPath := ""
   145  	if starterProject.SubDir != "" {
   146  		originalPath = path
   147  		path, err = os.MkdirTemp("", "")
   148  		if err != nil {
   149  			return err
   150  		}
   151  	}
   152  
   153  	_, err = git.PlainClone(path, false, cloneOptions)
   154  
   155  	if err != nil {
   156  
   157  		// it returns the following error if no matching ref found
   158  		// if we get this error, we are trying again considering revision as tag, only if revision is specified.
   159  		if _, ok := err.(git.NoMatchingRefSpecError); !ok || revision == "" {
   160  			return err
   161  		}
   162  
   163  		// try again to consider revision as tag name
   164  		cloneOptions.ReferenceName = plumbing.NewTagReferenceName(revision)
   165  		// remove if any .git folder downloaded in above try
   166  		_ = os.RemoveAll(filepath.Join(path, ".git"))
   167  		_, err = git.PlainClone(path, false, cloneOptions)
   168  		if err != nil {
   169  			return err
   170  		}
   171  	}
   172  
   173  	// we don't want to download project be a git repo
   174  	err = os.RemoveAll(filepath.Join(path, ".git"))
   175  	if err != nil {
   176  		// we don't need to return (fail) if this happens
   177  		log.Warning("Unable to delete .git from cloned starter project")
   178  	}
   179  
   180  	if starterProject.SubDir != "" {
   181  		err = util.GitSubDir(path, originalPath,
   182  			starterProject.SubDir)
   183  		if err != nil {
   184  			return err
   185  		}
   186  	}
   187  	if verbose {
   188  		downloadSpinner.End(true)
   189  	}
   190  
   191  	return nil
   192  
   193  }
   194  

View as plain text