From 1467eaaa5fb471897a8207dcfe7e2bf52bb586f3 Mon Sep 17 00:00:00 2001 From: Manoramsharma <84619980+Manoramsharma@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:21:02 +0530 Subject: [PATCH] [PRETEST]: Added support for bare repo in clone function of git module (#419) * Added support for bare repo in clone fn Signed-off-by: Manoramsharma * Fixes Signed-off-by: Manoramsharma * Fixed CI failure Signed-off-by: Manoramsharma * Added test scenarios for clone function Signed-off-by: Manoramsharma * fixed the failing test Signed-off-by: Manoramsharma --------- Signed-off-by: Manoramsharma --- pkg/git/git.go | 59 +++++--------- pkg/git/git_test.go | 194 ++++++++++++++++++++++++++++++-------------- 2 files changed, 154 insertions(+), 99 deletions(-) diff --git a/pkg/git/git.go b/pkg/git/git.go index e5cda1c1..6ece5128 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -110,40 +110,6 @@ func (cloneOpts *CloneOptions) Validate() error { return nil } -// Clone clones a git repository -func (cloneOpts *CloneOptions) cloneBare() (*git.Repository, error) { - args := []string{"clone", "--bare"} - if cloneOpts.Commit != "" { - args = append(args, "--no-checkout") - } - args = append(args, cloneOpts.RepoURL, cloneOpts.LocalPath) - - cmd := exec.Command("git", args...) - cmd.Stdout = cloneOpts.Writer - cmd.Stderr = cloneOpts.Writer - - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("failed to clone bare repository: %w", err) - } - - repo, err := git.PlainOpen(cloneOpts.LocalPath) - if err != nil { - return nil, err - } - - if cloneOpts.Commit != "" { - cmd = exec.Command("git", "update-ref", "HEAD", cloneOpts.Commit) - cmd.Dir = cloneOpts.LocalPath - cmd.Stdout = cloneOpts.Writer - cmd.Stderr = cloneOpts.Writer - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("failed to update HEAD to specified commit: %w", err) - } - } - - return repo, nil -} - // CheckoutFromBare checks out the specified reference from a bare repository func (cloneOpts *CloneOptions) CheckoutFromBare() error { if !cloneOpts.Bare { @@ -176,12 +142,31 @@ func (cloneOpts *CloneOptions) CheckoutFromBare() error { return nil } -// Clone clones a git repository +// Clone clones a git repository, handling both bare and non-bare options func (cloneOpts *CloneOptions) Clone() (*git.Repository, error) { if err := cloneOpts.Validate(); err != nil { return nil, err } + if cloneOpts.Bare { + // Use local git command to clone as bare repository + cmdArgs := []string{"clone", "--bare", cloneOpts.RepoURL, cloneOpts.LocalPath} + cmd := exec.Command("git", cmdArgs...) + + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to clone repository: %s, error: %w", string(output), err) + } + + repo, err := git.PlainOpen(cloneOpts.LocalPath) + if err != nil { + return nil, err + } + + return repo, nil + } + + // Default non-bare clone using go-getter url, err := cloneOpts.ForceGitUrl() if err != nil { return nil, err @@ -220,10 +205,6 @@ func CloneWithOpts(opts ...CloneOption) (*git.Repository, error) { return nil, err } - if cloneOpts.Bare { - return cloneOpts.cloneBare() - } - return cloneOpts.Clone() } diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go index 9e3f3be2..7625b409 100644 --- a/pkg/git/git_test.go +++ b/pkg/git/git_test.go @@ -2,7 +2,9 @@ package git import ( "bytes" + "fmt" "os" + "os/exec" "path/filepath" "testing" @@ -51,66 +53,138 @@ func TestValidateGitOptions(t *testing.T) { func TestCloneWithOptions(t *testing.T) { var buf bytes.Buffer - tmpdir, err := os.MkdirTemp("", "git") - tmpdir = filepath.Join(tmpdir, "git") - assert.Equal(t, err, nil) - defer func() { - rErr := os.RemoveAll(tmpdir) - assert.Equal(t, rErr, nil) - }() - - repo, err := CloneWithOpts( - WithRepoURL("https://github.com/KusionStack/catalog.git"), - WithCommit("4e59d5852cd7"), - WithWriter(&buf), - WithLocalPath(tmpdir), - ) - assert.Equal(t, err, nil) - - head, err := repo.Head() - assert.Equal(t, err, nil) - assert.Equal(t, head.Hash().String(), "4e59d5852cd76542f9f0ec65e5773ca9f4e02462") - assert.Equal(t, err, nil) -} - -func TestCloneWithOptionsBare(t *testing.T) { - var buf bytes.Buffer - tmpdir, err := os.MkdirTemp("", "git-bare") - assert.NilError(t, err) - defer func() { - rErr := os.RemoveAll(tmpdir) - assert.NilError(t, rErr) - }() - - commitSHA := "4e59d5852cd76542f9f0ec65e5773ca9f4e02462" - repo, err := CloneWithOpts( - WithRepoURL("https://github.com/KusionStack/catalog.git"), - WithCommit(commitSHA), - WithWriter(&buf), - WithLocalPath(tmpdir), - WithBare(true), - ) - if err != nil { - t.Fatalf("Failed to clone bare repository: %v\nBuffer contents:\n%s", err, buf.String()) - } - - // Verify that the repository is bare - config, err := repo.Config() - assert.NilError(t, err) - assert.Equal(t, config.Core.IsBare, true, "Expected repository to be bare") - - // Verify that the repository is bare by checking for the absence of a .git directory - // and the presence of HEAD file in the root directory - _, err = os.Stat(filepath.Join(tmpdir, ".git")) - assert.Assert(t, os.IsNotExist(err), "Expected .git directory to not exist in a bare repo") - - _, err = os.Stat(filepath.Join(tmpdir, "HEAD")) - assert.NilError(t, err, "Expected HEAD file to exist in bare repo root") - - // Verify that HEAD points to the specified commit - head, err := repo.Head() - assert.NilError(t, err) - assert.Equal(t, head.Hash().String(), commitSHA, "Expected HEAD to point to the specified commit") + // Test cloning a remote repo as a bare repo + t.Run("RemoteBareClone", func(t *testing.T) { + tmpdir, err := os.MkdirTemp("", "git_bare") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(tmpdir) + assert.Equal(t, rErr, nil) + }() + + _, err = CloneWithOpts( + WithRepoURL("https://github.com/KusionStack/catalog.git"), + WithLocalPath(tmpdir), + WithBare(true), // Set the Bare flag to true + ) + assert.Equal(t, err, nil) + + // Verify the directory is a bare repository + _, err = os.Stat(filepath.Join(tmpdir, "HEAD")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "config")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "objects")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "refs")) + assert.Equal(t, os.IsNotExist(err), false) + }) + + // Test cloning a remote repo as a normal repo and checking out a commit + t.Run("RemoteNonBareCloneWithCommit", func(t *testing.T) { + tmpdir, err := os.MkdirTemp("", "git_non_bare") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(tmpdir) + assert.Equal(t, rErr, nil) + }() + + repo, err := CloneWithOpts( + WithRepoURL("https://github.com/KusionStack/catalog.git"), + WithCommit("4e59d5852cd76542f9f0ec65e5773ca9f4e02462"), + WithWriter(&buf), + WithLocalPath(tmpdir), + WithBare(false), // Ensure the Bare flag is false + ) + assert.Equal(t, err, nil) + + head, err := repo.Head() + assert.Equal(t, err, nil) + assert.Equal(t, head.Hash().String(), "4e59d5852cd76542f9f0ec65e5773ca9f4e02462") + }) + + // Test cloning a bare repo as a bare repo + t.Run("LocalBareCloneAsBare", func(t *testing.T) { + // Setup a local bare repository + bareRepoPath, err := os.MkdirTemp("", "local_bare_repo") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(bareRepoPath) + assert.Equal(t, rErr, nil) + }() + cmd := exec.Command("git", "clone", "--bare", "https://github.com/KusionStack/catalog.git", bareRepoPath) + err = cmd.Run() + assert.Equal(t, err, nil) + + // Clone the local bare repository as a bare repository + tmpdir, err := os.MkdirTemp("", "clone_bare_repo") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(tmpdir) + assert.Equal(t, rErr, nil) + }() + + _, err = CloneWithOpts( + WithRepoURL(bareRepoPath), + WithLocalPath(tmpdir), + WithBare(true), // Set the Bare flag to true + ) + assert.Equal(t, err, nil) + + // Verify the directory is a bare repository + _, err = os.Stat(filepath.Join(tmpdir, "HEAD")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "config")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "objects")) + assert.Equal(t, os.IsNotExist(err), false) + + _, err = os.Stat(filepath.Join(tmpdir, "refs")) + assert.Equal(t, os.IsNotExist(err), false) + }) + + // Test cloning a bare repo as a normal repo and checking out a commit + t.Run("LocalBareCloneAsNonBareWithCommit", func(t *testing.T) { + // Setup a local bare repository + bareRepoPath, err := os.MkdirTemp("", "local_bare_repo") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(bareRepoPath) + assert.Equal(t, rErr, nil) + }() + cmd := exec.Command("git", "clone", "--bare", "https://github.com/KusionStack/catalog.git", bareRepoPath) + err = cmd.Run() + assert.Equal(t, err, nil) + + // Construct the file URL for the local bare repository + bareRepoURL := fmt.Sprintf("file://%s", bareRepoPath) + + // Clone the local bare repository as a normal repository and checkout a commit + tmpdir, err := os.MkdirTemp("", "clone_non_bare_repo") + assert.Equal(t, err, nil) + defer func() { + rErr := os.RemoveAll(tmpdir) + assert.Equal(t, rErr, nil) + }() + + repo, err := CloneWithOpts( + WithRepoURL(bareRepoURL), + WithCommit("4e59d5852cd76542f9f0ec65e5773ca9f4e02462"), + WithWriter(&buf), + WithLocalPath(tmpdir), + WithBare(false), // Ensure the Bare flag is false + ) + assert.Equal(t, err, nil) + + head, err := repo.Head() + assert.Equal(t, err, nil) + assert.Equal(t, head.Hash().String(), "4e59d5852cd76542f9f0ec65e5773ca9f4e02462") + }) } func TestCheckoutFromBare(t *testing.T) {