diff --git a/envbuilder.go b/envbuilder.go index fb6b69d0..ce980b85 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -14,7 +14,6 @@ import ( "maps" "net" "net/http" - "net/url" "os" "os/exec" "os/user" @@ -50,6 +49,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/sirupsen/logrus" "github.com/tailscale/hujson" + giturls "github.com/whilp/git-urls" "golang.org/x/xerrors" ) @@ -863,14 +863,19 @@ func Run(ctx context.Context, options Options) error { // for a given repository URL. func DefaultWorkspaceFolder(repoURL string) (string, error) { if repoURL == "" { - return "/workspaces/empty", nil + return EmptyWorkspaceDir, nil } - parsed, err := url.Parse(repoURL) + parsed, err := giturls.Parse(repoURL) if err != nil { return "", err } name := strings.Split(parsed.Path, "/") - return fmt.Sprintf("/workspaces/%s", name[len(name)-1]), nil + hasOwnerAndRepo := len(name) >= 2 + if !hasOwnerAndRepo { + return EmptyWorkspaceDir, nil + } + repo := strings.TrimSuffix(name[len(name)-1], ".git") + return fmt.Sprintf("/workspaces/%s", repo), nil } type userInfo struct { diff --git a/envbuilder_test.go b/envbuilder_test.go index e38f0a4d..6af599c9 100644 --- a/envbuilder_test.go +++ b/envbuilder_test.go @@ -9,11 +9,64 @@ import ( func TestDefaultWorkspaceFolder(t *testing.T) { t.Parallel() - dir, err := envbuilder.DefaultWorkspaceFolder("https://github.com/coder/coder") - require.NoError(t, err) - require.Equal(t, "/workspaces/coder", dir) - dir, err = envbuilder.DefaultWorkspaceFolder("") - require.NoError(t, err) - require.Equal(t, envbuilder.EmptyWorkspaceDir, dir) + successTests := []struct { + name string + gitURL string + expected string + }{ + { + name: "HTTP", + gitURL: "https://github.com/coder/envbuilder.git", + expected: "/workspaces/envbuilder", + }, + { + name: "SSH", + gitURL: "git@github.com:coder/envbuilder.git", + expected: "/workspaces/envbuilder", + }, + { + name: "username and password", + gitURL: "https://username:password@github.com/coder/envbuilder.git", + expected: "/workspaces/envbuilder", + }, + { + name: "fragment", + gitURL: "https://github.com/coder/envbuilder.git#feature-branch", + expected: "/workspaces/envbuilder", + }, + { + name: "empty", + gitURL: "", + expected: envbuilder.EmptyWorkspaceDir, + }, + } + for _, tt := range successTests { + t.Run(tt.name, func(t *testing.T) { + dir, err := envbuilder.DefaultWorkspaceFolder(tt.gitURL) + require.NoError(t, err) + require.Equal(t, tt.expected, dir) + }) + } + + invalidTests := []struct { + name string + invalidURL string + }{ + { + name: "simple text", + invalidURL: "not a valid URL", + }, + { + name: "website URL", + invalidURL: "www.google.com", + }, + } + for _, tt := range invalidTests { + t.Run(tt.name, func(t *testing.T) { + dir, err := envbuilder.DefaultWorkspaceFolder(tt.invalidURL) + require.NoError(t, err) + require.Equal(t, envbuilder.EmptyWorkspaceDir, dir) + }) + } } diff --git a/git.go b/git.go index c206bf5f..4aa9c541 100644 --- a/git.go +++ b/git.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net" - "net/url" "os" "strings" @@ -22,6 +21,7 @@ import ( gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/go-git/go-git/v5/storage/filesystem" "github.com/skeema/knownhosts" + giturls "github.com/whilp/git-urls" "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh" ) @@ -46,7 +46,7 @@ type CloneRepoOptions struct { // // The bool returned states whether the repository was cloned or not. func CloneRepo(ctx context.Context, opts CloneRepoOptions) (bool, error) { - parsed, err := url.Parse(opts.RepoURL) + parsed, err := giturls.Parse(opts.RepoURL) if err != nil { return false, fmt.Errorf("parse url %q: %w", opts.RepoURL, err) } diff --git a/go.mod b/go.mod index 703ee109..8eaa09c8 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/skeema/knownhosts v1.2.2 github.com/stretchr/testify v1.9.0 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a + github.com/whilp/git-urls v1.0.0 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.22.0 golang.org/x/sync v0.7.0 diff --git a/go.sum b/go.sum index d957a9b4..20e24369 100644 --- a/go.sum +++ b/go.sum @@ -842,6 +842,8 @@ github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vb github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I= github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ= +github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= +github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= diff --git a/integration/integration_test.go b/integration/integration_test.go index 7cd7fe27..3824c609 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -160,7 +160,7 @@ func TestSucceedsGitAuth(t *testing.T) { envbuilderEnv("GIT_PASSWORD", "testing"), }}) require.NoError(t, err) - gitConfig := execContainer(t, ctr, "cat /workspaces/.git/config") + gitConfig := execContainer(t, ctr, "cat /workspaces/empty/.git/config") require.Contains(t, gitConfig, srv.URL) } @@ -182,7 +182,7 @@ func TestSucceedsGitAuthInURL(t *testing.T) { envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"), }}) require.NoError(t, err) - gitConfig := execContainer(t, ctr, "cat /workspaces/.git/config") + gitConfig := execContainer(t, ctr, "cat /workspaces/empty/.git/config") require.Contains(t, gitConfig, u.String()) }