Skip to content

Commit

Permalink
Reimplement project-clone subPath handling to conform to devfile API
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
amisevsk committed Jun 23, 2023
1 parent 9f63702 commit f9955f2
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 11 deletions.
5 changes: 4 additions & 1 deletion project-clone/internal/devfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 5 additions & 5 deletions project-clone/internal/git/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}

Expand Down
33 changes: 28 additions & 5 deletions project-clone/internal/git/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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
}

Expand All @@ -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
}
27 changes: 27 additions & 0 deletions project-clone/internal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}

0 comments on commit f9955f2

Please sign in to comment.