telemetry: Normalize git URLs

Normalize `https://github.com/dagger/dagger` and
`git@github.com:dagger/dagger.git` to `github.com/dagger/dagger`

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2022-03-18 12:29:44 -07:00
parent fead547df1
commit f7628adee5
3 changed files with 100 additions and 6 deletions

65
telemetry/git.go Normal file
View File

@ -0,0 +1,65 @@
package telemetry
import (
"fmt"
"net/url"
"regexp"
"strings"
)
var (
urlSchemeRegExp = regexp.MustCompile(`^[^:]+://`)
scpLikeURLRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})(?:\/|:))?(?P<path>[^\\].*\/[^\\].*)$`)
)
func parseGitURL(endpoint string) (string, error) {
if e, ok := parseSCPLike(endpoint); ok {
return e, nil
}
return parseURL(endpoint)
}
func parseURL(endpoint string) (string, error) {
u, err := url.Parse(endpoint)
if err != nil {
return "", err
}
if !u.IsAbs() {
return "", fmt.Errorf(
"invalid endpoint: %s", endpoint,
)
}
return fmt.Sprintf("%s%s", u.Hostname(), u.Path), nil
}
func parseSCPLike(endpoint string) (string, bool) {
if matchesURLScheme(endpoint) || !matchesScpLike(endpoint) {
return "", false
}
_, host, _, path := findScpLikeComponents(endpoint)
return fmt.Sprintf("%s/%s", host, strings.TrimSuffix(path, ".git")), true
}
// matchesURLScheme returns true if the given string matches a URL-like
// format scheme.
func matchesURLScheme(url string) bool {
return urlSchemeRegExp.MatchString(url)
}
// matchesScpLike returns true if the given string matches an SCP-like
// format scheme.
func matchesScpLike(url string) bool {
return scpLikeURLRegExp.MatchString(url)
}
// findScpLikeComponents returns the user, host, port and path of the
// given SCP-like URL.
func findScpLikeComponents(url string) (user, host, port, path string) {
m := scpLikeURLRegExp.FindStringSubmatch(url)
return m[1], m[2], m[3], m[4]
}

22
telemetry/git_test.go Normal file
View File

@ -0,0 +1,22 @@
package telemetry
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestParseGit(t *testing.T) {
var (
endpoint string
err error
)
endpoint, err = parseGitURL("https://github.com/dagger/dagger")
require.NoError(t, err)
require.Equal(t, endpoint, "github.com/dagger/dagger")
endpoint, err = parseGitURL("git@github.com:dagger/dagger.git")
require.NoError(t, err)
require.Equal(t, endpoint, "github.com/dagger/dagger")
}

View File

@ -50,7 +50,7 @@ func Track(ctx context.Context, eventName string, properties ...*Property) {
return return
} }
repo := gitRepoURL(".") repo := gitRepoURL(ctx, ".")
// Base properties // Base properties
props := map[string]interface{}{ props := map[string]interface{}{
@ -194,8 +194,8 @@ func hash(s string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(s))) return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
} }
// // gitRepoURL returns the git repository remote, if any. // gitRepoURL returns the git repository remote, if any.
func gitRepoURL(path string) string { func gitRepoURL(ctx context.Context, path string) string {
repo, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{ repo, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{
DetectDotGit: true, DetectDotGit: true,
}) })
@ -208,9 +208,16 @@ func gitRepoURL(path string) string {
return "" return ""
} }
if urls := origin.Config().URLs; len(urls) > 0 { urls := origin.Config().URLs
return urls[0] if len(urls) == 0 {
return ""
} }
endpoint, err := parseGitURL(urls[0])
if err != nil {
log.Ctx(ctx).Debug().Err(err).Str("url", urls[0]).Msg("failed to parse git URL")
return "" return ""
}
return endpoint
} }