Skip to content

Commit

Permalink
feat: Enhance git repository handling in tests and core functionality
Browse files Browse the repository at this point in the history
- Introduced local repository support in the 'Clone' function to allow cloning from local paths.
- Updated 'IsValidURL' to validate local git repositories in addition to standard URLs.
- Enhanced test cases in 'git_test.go' and 'take_test.go' to create and utilize temporary git repositories for more robust testing.
- Improved error handling and reporting in the test setup for better reliability.

These changes improve the functionality of the 'take' command and its associated tests, ensuring better handling of both local and remote git repositories.

Signed-off-by: Alessandro De Blasis <[email protected]>
  • Loading branch information
deblasis committed Dec 10, 2024
1 parent f5d131d commit 01694ac
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 67 deletions.
28 changes: 25 additions & 3 deletions internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ type CloneOptions struct {

// Clone clones a git repository
func Clone(opts CloneOptions) error {
// For local repos, just copy the directory
if IsGitRepo(opts.URL) {
// Use git clone for local repos too to maintain git history
args := []string{"clone"}
if opts.Depth > 0 {
args = append(args, "--depth", fmt.Sprintf("%d", opts.Depth))
}
args = append(args, opts.URL, opts.TargetDir)

cmd := exec.Command("git", args...)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%w: %s", ErrCloneFailed, string(output))
}
return nil
}

if !IsValidURL(opts.URL) {
return ErrInvalidURL
}
Expand All @@ -49,15 +66,20 @@ func Clone(opts CloneOptions) error {
return nil
}

// IsValidURL checks if the given string is a valid git URL
// IsValidURL checks if the given string is a valid git URL or local repo path
func IsValidURL(url string) bool {
// Check if it's a local path that exists and is a git repo
if IsGitRepo(url) {
return true
}

// Check for SSH format (git@host:user/repo.git)
if strings.HasPrefix(url, "git@") && strings.Contains(url, ":") {
return strings.HasSuffix(url, ".git")
}

// Check for HTTPS format
if strings.HasPrefix(url, "https://") {
// Check for HTTPS/Git protocol
if strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "git://") {
return strings.HasSuffix(url, ".git")
}

Expand Down
65 changes: 62 additions & 3 deletions internal/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,23 @@ package git

import (
"os"
"os/exec"
"path/filepath"
"testing"
)

func TestIsValidURL(t *testing.T) {
// Create a temporary git repo for testing
tmpDir, err := os.MkdirTemp("", "git-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)

if err := exec.Command("git", "init", tmpDir).Run(); err != nil {
t.Fatalf("Failed to init test repo: %v", err)
}

tests := []struct {
name string
url string
Expand All @@ -22,6 +34,11 @@ func TestIsValidURL(t *testing.T) {
url: "[email protected]:user/repo.git",
want: true,
},
{
name: "valid local repo",
url: tmpDir,
want: true,
},
{
name: "invalid URL - no .git suffix",
url: "https://github.com/user/repo",
Expand Down Expand Up @@ -147,6 +164,48 @@ func TestClone(t *testing.T) {
}
defer os.RemoveAll(tmpDir)

// Create a test repo
testRepoDir, err := os.MkdirTemp("", "test-repo-*")
if err != nil {
t.Fatalf("Failed to create test repo dir: %v", err)
}
defer os.RemoveAll(testRepoDir)

// Initialize test repo
if err := exec.Command("git", "init", testRepoDir).Run(); err != nil {
t.Fatalf("Failed to init test repo: %v", err)
}

// Create a test file and commit it
testFile := filepath.Join(testRepoDir, "test.txt")
if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
t.Fatalf("Failed to create test file: %v", err)
}

cmd := exec.Command("git", "add", "test.txt")
cmd.Dir = testRepoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to add test file: %v", err)
}

cmd = exec.Command("git", "config", "user.email", "[email protected]")
cmd.Dir = testRepoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to configure git email: %v", err)
}

cmd = exec.Command("git", "config", "user.name", "Test User")
cmd.Dir = testRepoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to configure git username: %v", err)
}

cmd = exec.Command("git", "commit", "-m", "Initial commit")
cmd.Dir = testRepoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to commit: %v", err)
}

tests := []struct {
name string
opts CloneOptions
Expand All @@ -161,10 +220,10 @@ func TestClone(t *testing.T) {
wantErr: true,
},
{
name: "valid URL with target dir",
name: "valid local repo",
opts: CloneOptions{
URL: "https://github.com/octocat/Hello-World.git",
TargetDir: filepath.Join(tmpDir, "hello-world"),
URL: testRepoDir,
TargetDir: filepath.Join(tmpDir, "valid"),
Depth: 1,
},
wantErr: false,
Expand Down
46 changes: 19 additions & 27 deletions pkg/take/take.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/deblasis/take/internal/git"
)

var (
Expand Down Expand Up @@ -64,10 +65,10 @@ func Take(opts Options) Result {
return Result{Error: ErrInvalidPath}
}

// Handle URLs
if strings.Contains(opts.Path, "://") || strings.Contains(opts.Path, "@") {
// Handle URLs and git repos
if strings.Contains(opts.Path, "://") || strings.Contains(opts.Path, "@") || git.IsGitRepo(opts.Path) {
switch {
case urlPatterns.git.MatchString(opts.Path):
case git.IsGitRepo(opts.Path) || urlPatterns.git.MatchString(opts.Path):
return handleGitURL(opts)
case urlPatterns.tarball.MatchString(opts.Path):
return handleTarballURL(opts)
Expand Down Expand Up @@ -133,40 +134,31 @@ func handleDirectory(opts Options) Result {
}
}

// handleGitURL clones a git repository
// handleGitURL handles git repository cloning
func handleGitURL(opts Options) Result {
// Extract repository name from URL
repoName := filepath.Base(opts.Path)
repoName = strings.TrimSuffix(repoName, ".git")
repoName = strings.TrimSuffix(repoName, "/")
targetDir := filepath.Join(".", repoName)

// Remove target directory if it exists
os.RemoveAll(targetDir)

// Build git clone command
args := []string{"clone"}
if opts.GitCloneDepth > 0 {
args = append(args, "--depth", strconv.Itoa(opts.GitCloneDepth))
targetDir := git.GetRepoName(opts.Path)
if targetDir == "" {
targetDir = filepath.Base(opts.Path)
}
args = append(args, opts.Path, targetDir)

// Execute git clone
cmd := exec.Command("git", args...)
cmd.Stderr = os.Stderr // Show git output for debugging
if err := cmd.Run(); err != nil {
return Result{Error: fmt.Errorf("git clone failed: %v", err)}
err := git.Clone(git.CloneOptions{
URL: opts.Path,
TargetDir: targetDir,
Depth: opts.GitCloneDepth,
})

if err != nil {
return Result{Error: fmt.Errorf("failed to clone repository: %w", err)}
}

// Get absolute path
absPath, err := filepath.Abs(targetDir)
if err != nil {
return Result{Error: err}
}

return Result{
FinalPath: absPath,
WasCloned: true,
FinalPath: absPath,
WasCloned: true,
}
}

Expand Down
92 changes: 58 additions & 34 deletions pkg/take/take_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,49 @@ func createMockZip(t *testing.T) string {
return zipPath
}

// Create a mock git repository for testing
func createTestRepo(t *testing.T) string {
repoDir, err := os.MkdirTemp("", "test-repo-*")
if err != nil {
t.Fatalf("Failed to create test repo dir: %v", err)
}

if err := exec.Command("git", "init", repoDir).Run(); err != nil {
t.Fatalf("Failed to init test repo: %v", err)
}

testFile := filepath.Join(repoDir, "test.txt")
if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
t.Fatalf("Failed to create test file: %v", err)
}

cmd := exec.Command("git", "add", "test.txt")
cmd.Dir = repoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to add test file: %v", err)
}

cmd = exec.Command("git", "config", "user.email", "[email protected]")
cmd.Dir = repoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to configure git email: %v", err)
}

cmd = exec.Command("git", "config", "user.name", "Test User")
cmd.Dir = repoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to configure git username: %v", err)
}

cmd = exec.Command("git", "commit", "-m", "Initial commit")
cmd.Dir = repoDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to commit: %v", err)
}

return repoDir
}

func TestTake(t *testing.T) {
// Create mock archives
tarPath := createMockTarball(t)
Expand Down Expand Up @@ -123,7 +166,19 @@ func TestTake(t *testing.T) {
return filepath.Join(tmpDir, path)
}

tests := []testCase{
// Create test repo
testRepo := createTestRepo(t)
defer os.RemoveAll(testRepo)

tests := []struct {
name string
opts Options
setup func(t *testing.T)
cleanup func() error
wantResult Result
wantErr error
checkResult func(t *testing.T, got Result)
}{
{
name: "create new directory",
opts: Options{
Expand Down Expand Up @@ -195,24 +250,11 @@ func TestTake(t *testing.T) {
wantErr: ErrInvalidPath,
},
{
name: "handle git HTTPS URL",
name: "handle git repo",
opts: Options{
Path: "https://github.com/deblasis/take.git",
},
setup: func(t *testing.T) {
// Skip if no git credentials available
cmd := exec.Command("git", "config", "--get", "credential.helper")
if err := cmd.Run(); err != nil {
t.Skip("Git credentials not configured, skipping clone test")
}
// Clean up any existing clone
os.RemoveAll("take")
Path: testRepo,
},
checkResult: func(t *testing.T, got Result) {
// Skip validation if test was skipped
if t.Skipped() {
return
}
if got.Error != nil {
t.Errorf("Unexpected error: %v", got.Error)
return
Expand All @@ -222,24 +264,6 @@ func TestTake(t *testing.T) {
}
},
},
{
name: "handle git SSH URL",
opts: Options{
Path: "[email protected]:deblasis/take.git",
},
setup: func(t *testing.T) {
// Skip if no SSH key available
_, err := os.Stat(filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa"))
if err != nil {
t.Skip("SSH key not found, skipping SSH clone test")
}
},
checkResult: func(t *testing.T, got Result) {
if !got.WasCloned {
t.Error("Expected repository to be cloned")
}
},
},
{
name: "handle tarball URL",
opts: Options{
Expand Down

0 comments on commit 01694ac

Please sign in to comment.