Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Git package error handling #8345

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/lakectl/cmd/local_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/treeverse/lakefs/pkg/api/apigen"
"github.com/treeverse/lakefs/pkg/git"
giterror "github.com/treeverse/lakefs/pkg/git/errors"
"github.com/treeverse/lakefs/pkg/local"
"github.com/treeverse/lakefs/pkg/uri"
)
Expand Down Expand Up @@ -50,7 +51,7 @@ func localInit(ctx context.Context, dir string, remote *uri.URI, force, updateIg
gitDir, err := git.GetRepositoryPath(dir)
if err != nil {
// report an error only if it's not a git repository
if !errors.Is(err, git.ErrNotARepository) && !errors.Is(err, git.ErrNoGit) {
if !errors.Is(err, giterror.ErrNotARepository) && !errors.Is(err, giterror.ErrNoGit) {
return "", err
}
} else if gitDir == dir {
Expand Down Expand Up @@ -79,7 +80,7 @@ func localInit(ctx context.Context, dir string, remote *uri.URI, force, updateIg
ignoreFile, err := git.Ignore(dir, []string{dir}, []string{filepath.Join(dir, local.IndexFileName)}, local.IgnoreMarker)
if err == nil {
fmt.Println("Location added to", ignoreFile)
} else if !(errors.Is(err, git.ErrNotARepository) || errors.Is(err, git.ErrNoGit)) {
} else if !(errors.Is(err, giterror.ErrNotARepository) || errors.Is(err, giterror.ErrNoGit)) {
return "", err
}
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/lakectl/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/treeverse/lakefs/pkg/api/apiutil"
lakefsconfig "github.com/treeverse/lakefs/pkg/config"
"github.com/treeverse/lakefs/pkg/git"
giterror "github.com/treeverse/lakefs/pkg/git/errors"
"github.com/treeverse/lakefs/pkg/local"
"github.com/treeverse/lakefs/pkg/logging"
"github.com/treeverse/lakefs/pkg/osinfo"
Expand Down Expand Up @@ -282,7 +283,7 @@ func getSyncArgs(args []string, requireRemote bool, considerGitRoot bool) (remot
gitRoot, err := git.GetRepositoryPath(localPath)
if err == nil {
localPath = gitRoot
} else if !(errors.Is(err, git.ErrNotARepository) || errors.Is(err, git.ErrNoGit)) { // allow support in environments with no git
} else if !(errors.Is(err, giterror.ErrNotARepository) || errors.Is(err, giterror.ErrNoGit)) { // allow support in environments with no git
DieErr(err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/git/errors.go → pkg/git/errors/errors.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package git
package errors

import (
"errors"
Expand Down
25 changes: 7 additions & 18 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"text/template"

"github.com/treeverse/lakefs/pkg/fileutil"
giterror "github.com/treeverse/lakefs/pkg/git/errors"
"github.com/treeverse/lakefs/pkg/git/internal"
"golang.org/x/exp/slices"
)

Expand Down Expand Up @@ -39,7 +41,7 @@ type URL struct {
func git(dir string, args ...string) (string, int, error) {
_, err := exec.LookPath("git") // assume git is in the path, otherwise consider as not having git support
if err != nil {
return "", 0, ErrNoGit
return "", 0, giterror.ErrNoGit
}
cmd := exec.Command("git", args...)
cmd.Dir = dir
Expand All @@ -65,7 +67,7 @@ func IsRepository(dir string) bool {
// GetRepositoryPath Returns the git repository root path if dir is a directory inside a git repository, otherwise returns error
func GetRepositoryPath(dir string) (string, error) {
out, _, err := git(dir, "rev-parse", "--show-toplevel")
return handleOutput(out, err)
return internal.HandleOutput(out, err)
}

func createEntriesForIgnore(dir string, paths []string, exclude bool) ([]string, error) {
Expand Down Expand Up @@ -191,14 +193,14 @@ func Ignore(dir string, ignorePaths, excludePaths []string, marker string) (stri

func CurrentCommit(path string) (string, error) {
out, _, err := git(path, "rev-parse", "--short", "HEAD")
return handleOutput(out, err)
return internal.HandleOutput(out, err)
}

func MetadataFor(path, ref string) (map[string]string, error) {
kv := make(map[string]string)
kv["git_commit_id"] = ref
originURL, err := Origin(path)
if errors.Is(err, ErrRemoteNotFound) {
if errors.Is(err, giterror.ErrRemoteNotFound) {
return kv, nil // no additional data to add
} else if err != nil {
return kv, err
Expand Down Expand Up @@ -231,7 +233,7 @@ func Origin(path string) (string, error) {
// the exit status is 2"
return "", nil
}
return handleOutput(out, err)
return internal.HandleOutput(out, err)
}

func ParseURL(raw string) *URL {
Expand All @@ -245,16 +247,3 @@ func ParseURL(raw string) *URL {
Project: matches[RemoteRegex.SubexpIndex("project")],
}
}

func handleOutput(out string, err error) (string, error) {
switch {
case err == nil:
return strings.TrimSpace(out), nil
case strings.Contains(out, "not a git repository"):
return "", ErrNotARepository
case strings.Contains(out, "remote not found"):
return "", ErrRemoteNotFound
default:
return "", fmt.Errorf("%s: %w", out, err)
}
}
5 changes: 3 additions & 2 deletions pkg/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/stretchr/testify/require"
"github.com/treeverse/lakefs/pkg/git"
giterror "github.com/treeverse/lakefs/pkg/git/errors"
)

func TestIsRepository(t *testing.T) {
Expand Down Expand Up @@ -57,7 +58,7 @@ func TestGetRepositoryPath(t *testing.T) {
}()

_, err = git.GetRepositoryPath(tmpdir)
require.ErrorIs(t, err, git.ErrNotARepository)
require.ErrorIs(t, err, giterror.ErrNotARepository)
_, err = git.GetRepositoryPath(tmpFile.Name())
require.Error(t, err)

Expand Down Expand Up @@ -90,7 +91,7 @@ func TestIgnore(t *testing.T) {

// Test we can't ignore if not a git repo
_, err = git.Ignore(tmpdir, []string{}, []string{}, marker)
require.ErrorIs(t, err, git.ErrNotARepository)
require.ErrorIs(t, err, giterror.ErrNotARepository)
_, err = git.Ignore(tmpFile.Name(), []string{}, []string{}, marker)
require.Error(t, err)

Expand Down
22 changes: 22 additions & 0 deletions pkg/git/internal/shared.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package internal

import (
"fmt"
"strings"

"github.com/treeverse/lakefs/pkg/git/errors"
)

func HandleOutput(out string, err error) (string, error) {
lowerOut := strings.ToLower(out)
switch {
case err == nil:
return strings.TrimSpace(out), nil
case strings.Contains(lowerOut, "not a git repository"):
return "", errors.ErrNotARepository
case strings.Contains(lowerOut, "remote not found"):
return "", errors.ErrRemoteNotFound
default:
return "", fmt.Errorf("%s: %w", out, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odd: This error has a different format, it includes out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only place where the output has any relevance

}
}
63 changes: 63 additions & 0 deletions pkg/git/internal/shared_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package internal_test

import (
"errors"
"testing"

"github.com/stretchr/testify/require"
giterror "github.com/treeverse/lakefs/pkg/git/errors"
"github.com/treeverse/lakefs/pkg/git/internal"
)

var testErr = errors.New("this is a test generated error")

func TestHandleOutput(t *testing.T) {
testCases := []struct {
Name string
Output string
Error error
ExpectedError error
}{
{
Name: "not git repository",
Output: "fatal: not a git repository (or any of the parent directories): .git",
Error: testErr,
ExpectedError: giterror.ErrNotARepository,
},
{
Name: "not a git repository - Mount",
Output: "fatal: Not a git repository (or any parent up to mount point /home/my_home)\nStopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).",
Error: testErr,
ExpectedError: giterror.ErrNotARepository,
},
{
Name: "other error",
Output: "Some other error happened",
Error: testErr,
ExpectedError: testErr,
},
{
Name: "remote not found",
Output: "prefix, Remote nOt founD, suffix",
Error: testErr,
ExpectedError: giterror.ErrRemoteNotFound,
},
{
Name: "no error",
Output: "This is a valid response",
},
}

for _, tt := range testCases {
t.Run(tt.Name, func(t *testing.T) {
str, err := internal.HandleOutput(tt.Output, tt.Error)
require.ErrorIs(t, err, tt.ExpectedError)
if tt.ExpectedError != nil {
require.Equal(t, "", str)
} else {
require.Equal(t, tt.Output, str)
}

})
}
}
Loading