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

feat(git): log parsed gitURL and warn if local #345

Merged
merged 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
23 changes: 22 additions & 1 deletion git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type CloneRepoOptions struct {
Depth int
CABundle []byte
ProxyOptions transport.ProxyOptions
Logf log.Func
johnstcn marked this conversation as resolved.
Show resolved Hide resolved
}

// CloneRepo will clone the repository at the given URL into the given path.
Expand All @@ -52,6 +53,7 @@ func CloneRepo(ctx context.Context, opts CloneRepoOptions) (bool, error) {
if err != nil {
return false, fmt.Errorf("parse url %q: %w", opts.RepoURL, err)
}
opts.Logf(log.LevelInfo, "#1: Parsed Git URL as %q", parsed.Redacted())
if parsed.Hostname() == "dev.azure.com" {
// Azure DevOps requires capabilities multi_ack / multi_ack_detailed,
// which are not fully implemented and by default are included in
Expand All @@ -73,6 +75,7 @@ func CloneRepo(ctx context.Context, opts CloneRepoOptions) (bool, error) {
transport.UnsupportedCapabilities = []capability.Capability{
capability.ThinPack,
}
opts.Logf(log.LevelInfo, "#1: Workaround for Azure DevOps: marking thin-pack as unsupported")
}

err = opts.Storage.MkdirAll(opts.Path, 0o755)
Expand Down Expand Up @@ -203,6 +206,8 @@ func LogHostKeyCallback(logger log.Func) gossh.HostKeyCallback {
// | https?://host.tld/repo | Not Set | Set | HTTP Basic |
// | https?://host.tld/repo | Set | Not Set | HTTP Basic |
// | https?://host.tld/repo | Set | Set | HTTP Basic |
// | file://path/to/repo | - | - | None |
// | path/to/repo | - | - | None |
// | All other formats | - | - | SSH |
//
// For SSH authentication, the default username is "git" but will honour
Expand All @@ -219,7 +224,13 @@ func SetupRepoAuth(options *options.Options) transport.AuthMethod {
options.Logger(log.LevelInfo, "#1: ❔ No Git URL supplied!")
return nil
}
if strings.HasPrefix(options.GitURL, "http://") || strings.HasPrefix(options.GitURL, "https://") {
parsedURL, err := giturls.Parse(options.GitURL)
if err != nil {
options.Logger(log.LevelError, "#1: ❌ Failed to parse Git URL: %s", err.Error())
return nil
}

if parsedURL.Scheme == "http" || parsedURL.Scheme == "https" {
// Special case: no auth
if options.GitUsername == "" && options.GitPassword == "" {
options.Logger(log.LevelInfo, "#1: 👤 Using no authentication!")
Expand All @@ -235,6 +246,15 @@ func SetupRepoAuth(options *options.Options) transport.AuthMethod {
}
}

if parsedURL.Scheme == "file" {
// go-git will try to fallback to using the `git` command for local
// filesystem clones. However, it's more likely than not that the
// `git` command is not present in the container image. Log a warning
// but continue. Also, no auth.
options.Logger(log.LevelWarn, "#1: 🚧 Using local filesystem clone! This requires the git executable to be present!")
return nil
}

// Generally git clones over SSH use the 'git' user, but respect
// GIT_USERNAME if set.
if options.GitUsername == "" {
Expand Down Expand Up @@ -302,6 +322,7 @@ func CloneOptionsFromOptions(options options.Options) (CloneRepoOptions, error)
SingleBranch: options.GitCloneSingleBranch,
Depth: int(options.GitCloneDepth),
CABundle: caBundle,
Logf: options.Logger,
}

cloneOpts.RepoAuth = SetupRepoAuth(&options)
Expand Down
35 changes: 35 additions & 0 deletions git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func TestCloneRepo(t *testing.T) {
Path: "/",
RepoURL: srv.URL,
Storage: clientFS,
Logf: testLog(t),
})
require.NoError(t, err)
require.False(t, cloned)
Expand All @@ -117,6 +118,7 @@ func TestCloneRepo(t *testing.T) {
Username: tc.username,
Password: tc.password,
},
Logf: testLog(t),
})
require.Equal(t, tc.expectClone, cloned)
if tc.expectError != "" {
Expand Down Expand Up @@ -150,6 +152,7 @@ func TestCloneRepo(t *testing.T) {
Path: "/workspace",
RepoURL: authURL.String(),
Storage: clientFS,
Logf: testLog(t),
})
require.Equal(t, tc.expectClone, cloned)
if tc.expectError != "" {
Expand Down Expand Up @@ -199,6 +202,7 @@ func TestShallowCloneRepo(t *testing.T) {
Username: "test",
Password: "test",
},
Logf: testLog(t),
})
require.Error(t, err)
})
Expand Down Expand Up @@ -227,6 +231,7 @@ func TestShallowCloneRepo(t *testing.T) {
Username: "test",
Password: "test",
},
Logf: testLog(t),
})
require.NoError(t, err)
for _, path := range []string{"README.md", "foo", "baz"} {
Expand Down Expand Up @@ -264,6 +269,7 @@ func TestCloneRepoSSH(t *testing.T) {
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
},
},
Logf: testLog(t),
})
// TODO: ideally, we want to test the entire cloning flow.
// For now, this indicates successful ssh key auth.
Expand Down Expand Up @@ -296,6 +302,7 @@ func TestCloneRepoSSH(t *testing.T) {
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
},
},
Logf: testLog(t),
})
require.ErrorContains(t, err, "handshake failed")
require.False(t, cloned)
Expand Down Expand Up @@ -325,6 +332,7 @@ func TestCloneRepoSSH(t *testing.T) {
HostKeyCallback: gossh.FixedHostKey(randKeygen(t).PublicKey()),
},
},
Logf: testLog(t),
})
require.ErrorContains(t, err, "ssh: host key mismatch")
require.False(t, cloned)
Expand Down Expand Up @@ -453,6 +461,33 @@ func TestSetupRepoAuth(t *testing.T) {
auth := git.SetupRepoAuth(opts)
require.Nil(t, auth) // TODO: actually test SSH_AUTH_SOCK
})

t.Run("NoHostname/RepoOnly", func(t *testing.T) {
opts := &options.Options{
GitURL: "repo",
Logger: testLog(t),
}
auth := git.SetupRepoAuth(opts)
require.Nil(t, auth)
})

t.Run("NoHostname/Org/Repo", func(t *testing.T) {
opts := &options.Options{
GitURL: "org/repo",
Logger: testLog(t),
}
auth := git.SetupRepoAuth(opts)
require.Nil(t, auth)
})

t.Run("NoHostname/AbsolutePathish", func(t *testing.T) {
opts := &options.Options{
GitURL: "/org/repo",
Logger: testLog(t),
}
auth := git.SetupRepoAuth(opts)
require.Nil(t, auth)
})
}

func mustRead(t *testing.T, fs billy.Filesystem, path string) string {
Expand Down