Skip to content

Commit

Permalink
Backport fix from #497 to v2 (#498)
Browse files Browse the repository at this point in the history
* Recreate git config during update to prevent git config alteration

Backport fix from #497 to v2

* Bump version of Golang container to match module version

The Go module for v2 is 1.19, bumping the base docker image to match the
minimum version ensures go-getter can be compiled and executed on the
container. This change resolves the failing acceptance test for Samba.

```
Run docker exec -i gogetter bash -c "env ACC_SMB_TEST=1 go test -v ./... -run=TestSmb_"
  docker exec -i gogetter bash -c "env ACC_SMB_TEST=1 go test -v ./... -run=TestSmb_"
  shell: /usr/bin/bash -e {0}
  env:
    TEST_RESULTS_PATH: /tmp/test-results
Error: ./get_git.go:366:16: undefined: os.ReadDir
Error: ./get_git_test.go:886:9: undefined: os.WriteFile
Error: ./get_git_test.go:904:22: undefined: os.ReadFile
note: module requires Go 1.19
FAIL	github.com/hashicorp/go-getter/v2 [build failed]
?   	github.com/hashicorp/go-getter/v2/helper/testing	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/go-getter/v2/helper/url	0.006s [no tests to run]
FAIL
Error: Process completed with exit code 2.

```
  • Loading branch information
nywilken committed Jul 23, 2024
1 parent e310de3 commit dc7f6bc
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dockerfile to create a go-getter container with smbclient dependency that is used by the get_smb.go tests
FROM golang:1.15
FROM golang:1.19.13

COPY . /go-getter
WORKDIR /go-getter
Expand Down
83 changes: 70 additions & 13 deletions get_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (g *GitGetter) Get(ctx context.Context, req *Request) error {
return err
}
if err == nil {
err = g.update(ctx, req.Dst, sshKeyFile, ref, depth)
err = g.update(ctx, req.Dst, sshKeyFile, ref, req.URL(), depth)
} else {
err = g.clone(ctx, sshKeyFile, depth, req)
}
Expand Down Expand Up @@ -193,28 +193,63 @@ func (g *GitGetter) clone(ctx context.Context, sshKeyFile string, depth int, req
return getRunCommand(cmd)
}

func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, depth int) error {
// Determine if we're a branch. If we're NOT a branch, then we just
// switch to master prior to checking out
cmd := exec.CommandContext(ctx, "git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, u *url.URL, depth int) error {
// Remove all variations of .git directories
err := removeCaseInsensitiveGitDirectory(dst)
if err != nil {
return err

}

// Initialize the git repository
cmd := exec.CommandContext(ctx, "git", "init")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Add the git remote
cmd = exec.CommandContext(ctx, "git", "remote", "add", "origin", "--", u.String())
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Fetch the remote ref
cmd = exec.CommandContext(ctx, "git", "fetch", "--tags")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Fetch the remote ref
cmd = exec.CommandContext(ctx, "git", "fetch", "origin", "--", ref)
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

if getRunCommand(cmd) != nil {
// Not a branch, switch to default branch. This will also catch
// non-existent branches, in which case we want to switch to default
// and then checkout the proper branch later.
ref = findDefaultBranch(ctx, dst)
// Reset the branch to the fetched ref
cmd = exec.CommandContext(ctx, "git", "reset", "--hard", "FETCH_HEAD")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// We have to be on a branch to pull
// Checkout ref branch
if err := g.checkout(ctx, dst, ref); err != nil {
return err
}

if depth > 0 {
cmd = exec.CommandContext(ctx, "git", "pull", "--depth", strconv.Itoa(depth), "--ff-only")
cmd = exec.CommandContext(ctx, "git", "pull", "origin", "--depth", strconv.Itoa(depth), "--ff-only", "--", ref)
} else {
cmd = exec.CommandContext(ctx, "git", "pull", "--ff-only")
cmd = exec.CommandContext(ctx, "git", "pull", "origin", "--ff-only", "--", ref)
}

cmd.Dir = dst
Expand Down Expand Up @@ -326,6 +361,28 @@ func checkGitVersion(ctx context.Context, min string) error {
return nil
}

// removeCaseInsensitiveGitDirectory removes all .git directory variations
func removeCaseInsensitiveGitDirectory(dst string) error {
files, err := os.ReadDir(dst)
if err != nil {
return fmt.Errorf("Failed to read the destination directory %s during git update", dst)

}
for _, f := range files {
if strings.EqualFold(f.Name(), ".git") && f.IsDir() {
err := os.RemoveAll(filepath.Join(dst, f.Name()))
if err != nil {
return fmt.Errorf("Failed to remove the .git directory in the destination directory %s during git update", dst)

}

}

}
return nil

}

func (g *GitGetter) Detect(req *Request) (bool, error) {
src := req.Src
if len(src) == 0 {
Expand Down
149 changes: 149 additions & 0 deletions get_git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func TestGitGetter_remoteWithoutMaster(t *testing.T) {
}

// Get again should work
req.Dst = testing_helper.TempDir(t)
if err := g.Get(ctx, req); err != nil {
t.Fatalf("err: %s", err)
}
Expand Down Expand Up @@ -841,6 +842,154 @@ func TestGitGetter_subdirectory_traversal(t *testing.T) {
}
}

func TestGitGetter_BadGitConfig(t *testing.T) {
if !testHasGit {
t.Log("git not found, skipping")
t.Skip()

}

ctx := context.Background()
g := new(GitGetter)
dst := testing_helper.TempDir(t)

url, err := url.Parse("https://github.com/hashicorp/go-getter")
if err != nil {
t.Fatal(err)

}

_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
t.Fatalf(err.Error())
}

if err == nil {
// Update the repository containing the bad git config.
// This should remove the bad git config file and initialize a new one.
err = g.update(ctx, dst, testGitToken, "main", url, 1)

} else {
// Clone a repository with a git config file
req := &Request{
Dst: dst,
u: url,
}
err = g.clone(ctx, testGitToken, 1, req)
if err != nil {
t.Fatalf(err.Error())

}

// Edit the git config file to simulate a bad git config
gitConfigPath := filepath.Join(dst, ".git", "config")
err = os.WriteFile(gitConfigPath, []byte("bad config"), 0600)
if err != nil {
t.Fatalf(err.Error())

}

// Update the repository containing the bad git config.
// This should remove the bad git config file and initialize a new one.
err = g.update(ctx, dst, testGitToken, "main", url, 1)

}
if err != nil {
t.Fatalf(err.Error())

}

// Check if the .git/config file contains "bad config"
gitConfigPath := filepath.Join(dst, ".git", "config")
configBytes, err := os.ReadFile(gitConfigPath)
if err != nil {
t.Fatalf(err.Error())

}
if strings.Contains(string(configBytes), "bad config") {
t.Fatalf("The .git/config file contains 'bad config'")

}

}

func TestGitGetter_BadGitDirName(t *testing.T) {
if !testHasGit {
t.Log("git not found, skipping")
t.Skip()

}

ctx := context.Background()
g := new(GitGetter)
dst := testing_helper.TempDir(t)

url, err := url.Parse("https://github.com/hashicorp/go-getter")
if err != nil {
t.Fatal(err)

}

_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
t.Fatalf(err.Error())
}
if err == nil {
// Remove all variations of .git directories
err = removeCaseInsensitiveGitDirectory(dst)
if err != nil {
t.Fatalf(err.Error())

}

} else {
// Clone a repository with a git directory
req := &Request{
Dst: dst,
u: url,
}
err = g.clone(ctx, testGitToken, 1, req)
if err != nil {
t.Fatalf(err.Error())

}

// Rename the .git directory to .GIT
oldPath := filepath.Join(dst, ".git")
newPath := filepath.Join(dst, ".GIT")
err = os.Rename(oldPath, newPath)
if err != nil {
t.Fatalf(err.Error())

}

// Remove all variations of .git directories
err = removeCaseInsensitiveGitDirectory(dst)
if err != nil {
t.Fatalf(err.Error())

}

}
if err != nil {
t.Fatalf(err.Error())

}

// Check if the .GIT directory exists
if _, err := os.Stat(filepath.Join(dst, ".GIT")); !os.IsNotExist(err) {
t.Fatalf(".GIT directory still exists")

}

// Check if the .git directory exists
if _, err := os.Stat(filepath.Join(dst, ".git")); !os.IsNotExist(err) {
t.Fatalf(".git directory still exists")

}

}

// gitRepo is a helper struct which controls a single temp git repo.
type gitRepo struct {
t *testing.T
Expand Down

0 comments on commit dc7f6bc

Please sign in to comment.