From f9955f295d943f0f5109668d9f4e6ec3b30050c5 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Fri, 23 Jun 2023 14:43:48 -0400 Subject: [PATCH] Reimplement project-clone subPath handling to conform to devfile API The subDir field on starterProjects is intended to take one directory out of a starterProject rather than perform a sparse checkout. The previous subDir handling as been reworked into a regular sparse-checkout by renaming the attribute 'sparseCheckout'. The 'subDir' attribute now configures the project-clone container to 1. Clone the project to a temporary directory, set up remotes, check out a branch, etc. 2. Copy _the subdirectory specified by subDir_ from that project into $PROJECTS_ROOT This results in a folder within $PROJECTS_ROOT that is not a git repository and only contains the contents of that subfolder. Signed-off-by: Angel Misevski --- project-clone/internal/devfile.go | 5 +++- project-clone/internal/git/operations.go | 10 +++---- project-clone/internal/git/setup.go | 33 ++++++++++++++++++++---- project-clone/internal/utils.go | 27 +++++++++++++++++++ 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/project-clone/internal/devfile.go b/project-clone/internal/devfile.go index 76594ff7c..1133d18c1 100644 --- a/project-clone/internal/devfile.go +++ b/project-clone/internal/devfile.go @@ -27,7 +27,10 @@ import ( "github.com/devfile/devworkspace-operator/pkg/provision/metadata" ) -const ProjectSubDir = "subDir" +const ( + ProjectSparseCheckout = "sparseCheckout" + ProjectSubDir = "subDir" +) // GetClonePath gets the correct clonePath for a project, given the semantics in devfile/api func GetClonePath(project *dw.Project) string { diff --git a/project-clone/internal/git/operations.go b/project-clone/internal/git/operations.go index 1aa35742a..b165b8860 100644 --- a/project-clone/internal/git/operations.go +++ b/project-clone/internal/git/operations.go @@ -63,7 +63,7 @@ func CloneProject(project *dw.Project, projectPath string) error { } } - if project.Attributes.Exists(internal.ProjectSubDir) { + if project.Attributes.Exists(internal.ProjectSparseCheckout) { if err := shell.GitSparseCloneProject(defaultRemoteURL, defaultRemoteName, projectPath); err != nil { return fmt.Errorf("failed to sparsely git clone from %s: %s", defaultRemoteURL, err) } @@ -84,14 +84,14 @@ func SetupSparseCheckout(project *dw.Project, projectPath string) error { log.Printf("Setting up sparse checkout for project %s", project.Name) var err error - subdir := project.Attributes.GetString(internal.ProjectSubDir, &err) + sparseCheckoutDir := project.Attributes.GetString(internal.ProjectSparseCheckout, &err) if err != nil { - return fmt.Errorf("failed to read %s attribute on project %s", internal.ProjectSubDir, project.Name) + return fmt.Errorf("failed to read %s attribute on project %s", internal.ProjectSparseCheckout, project.Name) } - if subdir == "" { + if sparseCheckoutDir == "" { return nil } - if err := shell.GitSetupSparseCheckout(projectPath, subdir); err != nil { + if err := shell.GitSetupSparseCheckout(projectPath, sparseCheckoutDir); err != nil { return fmt.Errorf("error running sparse-checkout set: %w", err) } diff --git a/project-clone/internal/git/setup.go b/project-clone/internal/git/setup.go index 7afb1bdc3..86baf5c97 100644 --- a/project-clone/internal/git/setup.go +++ b/project-clone/internal/git/setup.go @@ -50,7 +50,7 @@ func doInitialGitClone(project *dw.Project) error { return fmt.Errorf("failed to clone project: %s", err) } - if project.Attributes.Exists(internal.ProjectSubDir) { + if project.Attributes.Exists(internal.ProjectSparseCheckout) { if err := SetupSparseCheckout(project, tmpClonePath); err != nil { return fmt.Errorf("failed to set up sparse checkout on project %s: %w", project.Name, err) } @@ -71,11 +71,10 @@ func doInitialGitClone(project *dw.Project) error { return fmt.Errorf("failed to checkout revision: %s", err) } - projectPath := path.Join(internal.ProjectsRoot, internal.GetClonePath(project)) - log.Printf("Moving cloned project %s from temporary dir %s to %s", project.Name, tmpClonePath, projectPath) - if err := os.Rename(tmpClonePath, projectPath); err != nil { - return fmt.Errorf("failed to move cloned project to PROJECTS_ROOT: %w", err) + if err := copyProjectFromTmpDir(project, tmpClonePath); err != nil { + return err } + return nil } @@ -92,3 +91,27 @@ func setupRemotesForExistingProject(project *dw.Project) error { } return nil } + +func copyProjectFromTmpDir(project *dw.Project, tmpClonePath string) error { + if project.Attributes.Exists(internal.ProjectSubDir) { + // Only want one directory from the project + var err error + subDirSubPath := project.Attributes.GetString(internal.ProjectSubDir, &err) + if err != nil { + return fmt.Errorf("failed to projects subDir on project: %w", err) + } + subDirPath := path.Join(tmpClonePath, subDirSubPath) + projectPath := path.Join(internal.ProjectsRoot, internal.GetClonePath(project)) + log.Printf("Moving subdirectory %s in project %s from temporary directory to %s", subDirSubPath, project.Name, projectPath) + if err := os.Rename(subDirPath, projectPath); err != nil { + return fmt.Errorf("failed to move subdirectory of cloned project to %s: %w", internal.ProjectsRoot, err) + } + } else { + projectPath := path.Join(internal.ProjectsRoot, internal.GetClonePath(project)) + log.Printf("Moving cloned project %s from temporary directory %s to %s", project.Name, tmpClonePath, projectPath) + if err := os.Rename(tmpClonePath, projectPath); err != nil { + return fmt.Errorf("failed to move cloned project to %s: %w", internal.ProjectsRoot, err) + } + } + return nil +} diff --git a/project-clone/internal/utils.go b/project-clone/internal/utils.go index 5ee83ef79..fe6bb4bde 100644 --- a/project-clone/internal/utils.go +++ b/project-clone/internal/utils.go @@ -32,6 +32,9 @@ import ( // The git repo can have additional remotes -- they will be ignored here. If both the project and git repo have remote // A configured, but the corresponding remote URL is different, needRemotes will be true. func CheckProjectState(project *dw.Project) (needClone, needRemotes bool, err error) { + if project.Attributes.Exists(ProjectSubDir) { + return checkSubPathProjectState(project) + } repo, err := OpenRepo(path.Join(ProjectsRoot, GetClonePath(project))) if err != nil { return false, false, err @@ -90,3 +93,27 @@ func DirExists(dir string) (bool, error) { } return false, fmt.Errorf("path %s already exists and is not a directory", dir) } + +func checkSubPathProjectState(project *dw.Project) (needClone, needRemotes bool, err error) { + // Check that attribute is valid and parseable + var attrErr error + _ = project.Attributes.GetString(ProjectSubDir, &attrErr) + if err != nil { + return false, false, fmt.Errorf("failed to read %s attribute on project %s: %w", ProjectSubDir, project.Name, err) + } + + // Result here won't be a git repository, so we only check whether the directory exists + clonePath := path.Join(ProjectsRoot, GetClonePath(project)) + stat, err := os.Stat(clonePath) + if err != nil { + if os.IsNotExist(err) { + return true, false, nil + } + return false, false, err + } + if stat.IsDir() { + return false, false, nil + } else { + return false, false, fmt.Errorf("could not check project state for project %s -- path %s exists and is not a directory", project.Name, clonePath) + } +}