-
Notifications
You must be signed in to change notification settings - Fork 58
fix: improve git URL parsing #486
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
Changes from 4 commits
83c4567
5298b6f
61c6d08
8b28b71
7549a40
4ad93b8
85928c9
3618988
0993246
d5a3784
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
| "github.com/go-git/go-billy/v5" | ||
| "github.com/go-git/go-billy/v5/memfs" | ||
| "github.com/go-git/go-billy/v5/osfs" | ||
| gittransport "github.com/go-git/go-git/v5/plumbing/transport" | ||
| githttp "github.com/go-git/go-git/v5/plumbing/transport/http" | ||
| gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh" | ||
| "github.com/stretchr/testify/require" | ||
|
|
@@ -140,9 +141,9 @@ func TestCloneRepo(t *testing.T) { | |
| authMW := mwtest.BasicAuthMW(tc.srvUsername, tc.srvPassword) | ||
| srv := httptest.NewServer(authMW(gittest.NewServer(srvFS))) | ||
|
|
||
| authURL, err := url.Parse(srv.URL) | ||
| authURL, err := gittransport.NewEndpoint(srv.URL) | ||
| require.NoError(t, err) | ||
| authURL.User = url.UserPassword(tc.username, tc.password) | ||
| authURL.User = url.UserPassword(tc.username, tc.password).String() | ||
| clientFS := memfs.New() | ||
|
|
||
| cloned, err := git.CloneRepo(context.Background(), t.Logf, git.CloneRepoOptions{ | ||
|
|
@@ -238,10 +239,9 @@ func TestShallowCloneRepo(t *testing.T) { | |
| func TestCloneRepoSSH(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| t.Run("AuthSuccess", func(t *testing.T) { | ||
| t.Run("Success", func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| // TODO: test the rest of the cloning flow. This just tests successful auth. | ||
| tmpDir := t.TempDir() | ||
| srvFS := osfs.New(tmpDir, osfs.WithChrootOS()) | ||
|
|
||
|
|
@@ -264,10 +264,9 @@ func TestCloneRepoSSH(t *testing.T) { | |
| }, | ||
| }, | ||
| }) | ||
| // TODO: ideally, we want to test the entire cloning flow. | ||
| // For now, this indicates successful ssh key auth. | ||
| require.ErrorContains(t, err, "repository not found") | ||
| require.False(t, cloned) | ||
| require.NoError(t, err) | ||
| require.True(t, cloned) | ||
| require.Equal(t, "Hello, world!", mustRead(t, clientFS, "/workspace/README.md")) | ||
|
Comment on lines
+287
to
+289
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
| }) | ||
|
|
||
| t.Run("AuthFailure", func(t *testing.T) { | ||
|
|
@@ -404,7 +403,7 @@ func TestSetupRepoAuth(t *testing.T) { | |
| } | ||
| auth := git.SetupRepoAuth(t.Logf, opts) | ||
| _, ok := auth.(*gitssh.PublicKeys) | ||
| require.True(t, ok) | ||
| require.True(t, ok, "expected SSH auth for git:// URL") | ||
| }) | ||
|
|
||
| t.Run("SSH/GitUsername", func(t *testing.T) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -434,14 +434,16 @@ func TestGitSSHAuth(t *testing.T) { | |
| _ = gittest.NewRepo(t, srvFS, gittest.Commit(t, "Dockerfile", "FROM "+testImageAlpine, "Initial commit")) | ||
| tr := gittest.NewServerSSH(t, srvFS, signer.PublicKey()) | ||
|
|
||
| _, err = runEnvbuilder(t, runOpts{env: []string{ | ||
| ctr, err := runEnvbuilder(t, runOpts{env: []string{ | ||
| envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), | ||
| envbuilderEnv("GIT_URL", tr.String()+"."), | ||
| envbuilderEnv("GIT_URL", tr.String()), | ||
| envbuilderEnv("GIT_SSH_PRIVATE_KEY_BASE64", base64Key), | ||
| }}) | ||
| // TODO: Ensure it actually clones but this does mean we have | ||
| // successfully authenticated. | ||
| require.ErrorContains(t, err, "repository not found") | ||
| require.NoError(t, err) | ||
| dockerFilePath := execContainer(t, ctr, "find /workspaces -name Dockerfile") | ||
| require.NotEmpty(t, dockerFilePath) | ||
| dockerFile := execContainer(t, ctr, "cat "+dockerFilePath) | ||
| require.Contains(t, dockerFile, testImageAlpine) | ||
|
Comment on lines
+442
to
+446
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
| }) | ||
|
|
||
| t.Run("Base64/Failure", func(t *testing.T) { | ||
|
|
||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't add new tests for this function as I figured the existing tests should suffice. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package ebutil | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net/url" | ||
| "strings" | ||
|
|
||
| gittransport "github.com/go-git/go-git/v5/plumbing/transport" | ||
| ) | ||
|
|
||
| type ParsedURL struct { | ||
| Protocol string | ||
| User string | ||
| Password string | ||
| Host string | ||
| Port int | ||
| Path string | ||
| Reference string | ||
| } | ||
|
|
||
| // ParseRepoURL parses the given repository URL into its components. | ||
| // We used to use chainguard-dev/git-urls for this, but its behaviour | ||
| // diverges from the go-git URL parser. To ensure consistency, we now | ||
| // use go-git directly with some tweaks. | ||
| func ParseRepoURL(repoURL string) (*ParsedURL, error) { | ||
| repoURL = fixupScheme(repoURL, "ssh://") | ||
| repoURL = fixupScheme(repoURL, "git://") | ||
| repoURL = fixupScheme(repoURL, "git+ssh://") | ||
| parsed, err := gittransport.NewEndpoint(repoURL) | ||
|
johnstcn marked this conversation as resolved.
Outdated
|
||
| if err != nil { | ||
| return nil, fmt.Errorf("parse repo url %q: %w", repoURL, err) | ||
| } | ||
| // Trim #reference from path | ||
| var reference string | ||
| if len(parsed.Path) > 0 { // annoyingly, strings.Index returns 0 if len(s) == 0 | ||
| if idx := strings.Index(parsed.Path, "#"); idx > -1 { | ||
| reference = parsed.Path[idx+1:] | ||
| parsed.Path = parsed.Path[:idx] | ||
| } | ||
| } | ||
|
johnstcn marked this conversation as resolved.
Outdated
|
||
| return &ParsedURL{ | ||
| Protocol: parsed.Protocol, | ||
| User: parsed.User, | ||
| Password: parsed.Password, | ||
| Host: parsed.Host, | ||
| Port: parsed.Port, | ||
| Path: parsed.Path, | ||
| Reference: reference, | ||
| }, nil | ||
| } | ||
|
|
||
| func fixupScheme(repoURL, scheme string) string { | ||
| // go-git tries to handle protocol:// URLs with url.Parse. This fails | ||
| // in the case of e.g. (ssh|git)://git@host:user/path.git | ||
| if cut, found := strings.CutPrefix(repoURL, scheme); found { | ||
| if _, err := url.Parse(repoURL); err != nil && strings.Contains(err.Error(), "invalid port") { | ||
|
johnstcn marked this conversation as resolved.
Outdated
|
||
| return cut | ||
| } | ||
| } | ||
| return repoURL | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,8 +165,9 @@ func NewServerSSH(t *testing.T, fs billy.Filesystem, pubkeys ...gossh.PublicKey) | |
|
|
||
| addr, ok := l.Addr().(*net.TCPAddr) | ||
| require.True(t, ok) | ||
| tr, err := transport.NewEndpoint(fmt.Sprintf("ssh://git@%s:%d/", addr.IP, addr.Port)) | ||
| tr, err := transport.NewEndpoint(fmt.Sprintf("ssh://git@%s:%d%s", addr.IP, addr.Port, fs.Root())) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think previous me was to blame for this, for which I humbly apologise. |
||
| require.NoError(t, err) | ||
| t.Logf("git-ssh url: %s", tr.String()) | ||
| return tr | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have tests to verify that whitespace won't cause issues here for go-git?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leading and trailing whitespace can cause issues, but whitespace in the URL itself appears to be handled 'correctly'. I've added some tests + added logic to return the "cleaned" URL (with whitespace and
#reftrimmed) in 3618988.