diff --git a/pkg/git/client.go b/pkg/git/client.go index 61b67a7f..97203f63 100644 --- a/pkg/git/client.go +++ b/pkg/git/client.go @@ -10,6 +10,7 @@ import ( type Client interface { Checkout(repo Repository, commit string) error Clone(url, dst string) (*Repository, error) + OpenRepository(path string) (*Repository, error) } type ClientImpl struct{} @@ -46,3 +47,11 @@ func (c *ClientImpl) Clone(url, dst string) (*Repository, error) { } return NewLocalRepository(dst) } + +func (c *ClientImpl) OpenRepository(path string) (*Repository, error) { + _, err := git.PlainOpen(path) + if err != nil { + return nil, fmt.Errorf("failed to open git repository at %s: %w", path, err) + } + return NewLocalRepository(path) +} diff --git a/pkg/git/mocks/mock_client.go b/pkg/git/mocks/mock_client.go index 26c4106b..03863f1b 100644 --- a/pkg/git/mocks/mock_client.go +++ b/pkg/git/mocks/mock_client.go @@ -25,6 +25,12 @@ func (m *MockClient) Clone(url, dst string) (*git.Repository, error) { return args.Get(0).(*git.Repository), args.Error(1) } +// OpenRepository mocks the OpenRepository method of the Client interface +func (m *MockClient) OpenRepository(path string) (*git.Repository, error) { + args := m.Called(path) + return args.Get(0).(*git.Repository), args.Error(1) +} + // NewMockClient creates and returns a new instance of MockClient func NewMockClient() *MockClient { return new(MockClient) diff --git a/pkg/git/repository.go b/pkg/git/repository.go index 102d69be..3decbec9 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -29,3 +29,7 @@ func NewLocalRepository(path string) (*Repository, error) { } return &Repository{URL: *u}, nil } + +func (r Repository) IsLocal() bool { + return r.URL.Scheme == "file" +} diff --git a/pkg/lsp/mocks/mock_language_detector.go b/pkg/lsp/mocks/mock_language_detector.go new file mode 100644 index 00000000..81626681 --- /dev/null +++ b/pkg/lsp/mocks/mock_language_detector.go @@ -0,0 +1,29 @@ +package mocks + +import ( + "github.com/hide-org/hide/pkg/model" + "github.com/stretchr/testify/mock" +) + +type MockLanguageDetector struct { + mock.Mock +} + +func (m *MockLanguageDetector) DetectLanguage(file *model.File) string { + args := m.Called(file) + return args.String(0) +} + +func (m *MockLanguageDetector) DetectMainLanguage(files []*model.File) string { + args := m.Called(files) + return args.String(0) +} + +func (m *MockLanguageDetector) DetectLanguages(files []*model.File) map[string]int { + args := m.Called(files) + var result map[string]int + if r := args.Get(0); r != nil { + result = r.(map[string]int) + } + return result +} diff --git a/pkg/project/manager.go b/pkg/project/manager.go index 52643421..03b30c8a 100644 --- a/pkg/project/manager.go +++ b/pkg/project/manager.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/fs" + "net/url" "os" "path" "sync" @@ -102,20 +103,44 @@ func NewProjectManager( func (pm ManagerImpl) CreateProject(ctx context.Context, request CreateProjectRequest) (*model.Project, error) { log.Debug().Msgf("Creating project for repo %s", request.Repository.Url) + var ( + r *git.Repository + err error + projectPath string + ) + + // Generate unique project ID projectId := pm.randomString(10) - projectPath := path.Join(pm.projectsRoot, projectId) - // Clone git repo - if err := pm.createProjectDir(projectPath); err != nil { - log.Error().Err(err).Msg("Failed to create project directory") - return nil, fmt.Errorf("Failed to create project directory: %w", err) + repoUrl, err := url.Parse(request.Repository.Url) + if err != nil { + log.Error().Err(err).Msg("Failed to parse repository URL") + return nil, fmt.Errorf("Failed to parse repository URL: %w", err) } + repo := git.NewRepository(*repoUrl) - r, err := pm.git.Clone(request.Repository.Url, projectPath) - if err != nil { - log.Error().Err(err).Msg("Failed to clone git repo") - removeProjectDir(projectPath) - return nil, fmt.Errorf("Failed to clone git repo: %w", err) + if repo.IsLocal() { + // For local repositories, use the existing directory + r, err = pm.git.OpenRepository(repo.URL.Path) + if err != nil { + log.Error().Err(err).Msg("Failed to open local git repo") + return nil, fmt.Errorf("Failed to open local git repo: %w", err) + } + projectPath = repo.URL.Path + } else { + // For remote repositories, create directory and clone + projectPath = path.Join(pm.projectsRoot, projectId) + + if err := pm.createProjectDir(projectPath); err != nil { + log.Error().Err(err).Msg("Failed to create project directory") + return nil, fmt.Errorf("Failed to create project directory: %w", err) + } + r, err = pm.git.Clone(request.Repository.Url, projectPath) + if err != nil { + log.Error().Err(err).Msg("Failed to clone git repo") + removeProjectDir(projectPath) + return nil, fmt.Errorf("Failed to clone git repo: %w", err) + } } if request.Repository.Commit != nil { @@ -126,30 +151,26 @@ func (pm ManagerImpl) CreateProject(ctx context.Context, request CreateProjectRe } } - // Start devcontainer - var devContainerConfig devcontainer.Config - - if request.DevContainer != nil { - devContainerConfig = *request.DevContainer - } else { + // Configure devcontainer + devContainerConfig := request.DevContainer + if devContainerConfig == nil { config, err := pm.configFromProject(os.DirFS(projectPath)) if err != nil { log.Error().Err(err).Msgf("Failed to get devcontainer config from repository %s", request.Repository.Url) removeProjectDir(projectPath) return nil, fmt.Errorf("Failed to read devcontainer.json: %w", err) } - - devContainerConfig = config + devContainerConfig = &config } - containerId, err := pm.devContainerRunner.Run(ctx, projectPath, devContainerConfig) + containerId, err := pm.devContainerRunner.Run(ctx, projectPath, *devContainerConfig) if err != nil { log.Error().Err(err).Msg("Failed to launch devcontainer") removeProjectDir(projectPath) return nil, fmt.Errorf("Failed to launch devcontainer: %w", err) } - project := model.NewProject(projectId, projectPath, model.Config{DevContainerConfig: devContainerConfig}, containerId, request.Repository) + project := model.NewProject(projectId, projectPath, model.Config{DevContainerConfig: *devContainerConfig}, containerId, request.Repository) languages := request.Languages if len(languages) == 0 {