Files
odo/pkg/registry/starter_project.go
Armel Soro 28ed064133 Do not return an error in odo analyze if current directory contains an invalid Devfile (#6905)
* Add unit test highliging the issue

* Fix 'delete' unit tests

* Pass the filesystem object where it is relevant

* Add a way for CLI commands to indicate whether of not they require a valid Devfile

For the 'analyze' command, this is not required,
so Devfile parsing will be ignored completely.

* Make the fake filesystem return an absolute current dir

Otherwise, some code will assume it is relative,
and try to prepend the current physical directory
2023-06-22 10:06:18 +02:00

194 lines
5.2 KiB
Go

package registry
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/util"
)
const (
RegistryUser = "default"
)
func checkoutProject(subDir, zipURL, path, starterToken string, fsys filesystem.Filesystem) error {
if subDir == "" {
subDir = "/"
}
err := util.GetAndExtractZip(zipURL, path, subDir, starterToken, fsys)
if err != nil {
return fmt.Errorf("failed to download and extract project zip folder: %w", err)
}
return nil
}
// DownloadStarterProject downloads a starter project referenced in devfile
// This will first remove the content of the contextDir
func DownloadStarterProject(fs filesystem.Filesystem, starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error {
var path string
var err error
// Retrieve the working directory in order to clone correctly
if contextDir == "" {
path, err = os.Getwd()
if err != nil {
return fmt.Errorf("could not get the current working directory: %w", err)
}
} else {
path = contextDir
}
// We will check to see if the project has a valid directory
err = util.IsValidProjectDir(path, location.DevfileLocation(fs, ""), fs)
if err != nil {
return err
}
if verbose {
log.Info("\nStarter Project")
}
if starterProject.Git != nil {
err := downloadGitProject(starterProject, decryptedToken, path, verbose)
if err != nil {
return err
}
} else if starterProject.Zip != nil {
url := starterProject.Zip.Location
sparseDir := starterProject.SubDir
var downloadSpinner *log.Status
if verbose {
downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, url)
}
err := checkoutProject(sparseDir, url, path, decryptedToken, fs)
if err != nil {
if verbose {
downloadSpinner.End(false)
}
return err
}
if verbose {
downloadSpinner.End(true)
}
} else {
return errors.New("project type not supported")
}
return nil
}
// downloadGitProject downloads the git starter projects from devfile.yaml
func downloadGitProject(starterProject *devfilev1.StarterProject, starterToken, path string, verbose bool) error {
remoteName, remoteUrl, revision, err := parsercommon.GetDefaultSource(starterProject.Git.GitLikeProjectSource)
if err != nil {
return fmt.Errorf("unable to get default project source for starter project %s: %w", starterProject.Name, err)
}
// convert revision to referenceName type, ref name could be a branch or tag
// if revision is not specified it would be the default branch of the project
refName := plumbing.ReferenceName(revision)
if plumbing.IsHash(revision) {
// Specifying commit in the reference name is not supported by the go-git library
// while doing git.PlainClone()
log.Warning("Specifying commit in 'revision' is not yet supported in odo.")
// overriding revision to empty as we do not support this
revision = ""
}
if revision != "" {
// lets consider revision to be a branch name first
refName = plumbing.NewBranchReferenceName(revision)
}
var downloadSpinner *log.Status
if verbose {
downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, remoteUrl)
defer downloadSpinner.End(false)
}
cloneOptions := &git.CloneOptions{
URL: remoteUrl,
RemoteName: remoteName,
// we don't need history for starter projects
Depth: 1,
}
if refName != "" {
cloneOptions.ReferenceName = refName
cloneOptions.SingleBranch = true
}
if starterToken != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: RegistryUser,
Password: starterToken,
}
}
originalPath := ""
if starterProject.SubDir != "" {
originalPath = path
path, err = os.MkdirTemp("", "")
if err != nil {
return err
}
}
_, err = git.PlainClone(path, false, cloneOptions)
if err != nil {
// it returns the following error if no matching ref found
// if we get this error, we are trying again considering revision as tag, only if revision is specified.
if _, ok := err.(git.NoMatchingRefSpecError); !ok || revision == "" {
return err
}
// try again to consider revision as tag name
cloneOptions.ReferenceName = plumbing.NewTagReferenceName(revision)
// remove if any .git folder downloaded in above try
_ = os.RemoveAll(filepath.Join(path, ".git"))
_, err = git.PlainClone(path, false, cloneOptions)
if err != nil {
return err
}
}
// we don't want to download project be a git repo
err = os.RemoveAll(filepath.Join(path, ".git"))
if err != nil {
// we don't need to return (fail) if this happens
log.Warning("Unable to delete .git from cloned starter project")
}
if starterProject.SubDir != "" {
err = util.GitSubDir(path, originalPath,
starterProject.SubDir)
if err != nil {
return err
}
}
if verbose {
downloadSpinner.End(true)
}
return nil
}