diff --git a/pkg/frontend/vcs/source/find_go.go b/pkg/frontend/vcs/source/find_go.go index bd20b3dab7..cf1b9418f2 100644 --- a/pkg/frontend/vcs/source/find_go.go +++ b/pkg/frontend/vcs/source/find_go.go @@ -6,6 +6,7 @@ import ( "fmt" "path" "path/filepath" + "strconv" "strings" "time" @@ -201,6 +202,18 @@ func (ff FileFinder) tryFindGoFile(ctx context.Context, maxAttempts int) (*vcsv1 path = path[len(repoPath)+pos:] } + // strip Go module major version segment (e.g. "v2/"), if present. + // GitHub repo paths don't include the major version directory, but Go + // symbol paths do (e.g. "github.com/grafana/pyroscope/v2/pkg/foo.go"). + path = strings.TrimLeft(path, "/") + if i := strings.IndexByte(path, '/'); i > 0 { + if seg := path[:i]; len(seg) > 1 && seg[0] == 'v' && isDigits(seg[1:]) { + if majorVer, _ := strconv.Atoi(seg[1:]); majorVer >= 2 { + path = path[i+1:] + } + } + } + // now try to find file in repo path = strings.TrimLeft(path, "/") attempts := 0 @@ -231,3 +244,12 @@ func (ff FileFinder) tryFindGoFile(ctx context.Context, maxAttempts int) (*vcsv1 return newFileResponse(content.Content, content.URL) } } + +func isDigits(s string) bool { + for _, c := range s { + if c < '0' || c > '9' { + return false + } + } + return len(s) > 0 +} diff --git a/pkg/frontend/vcs/source/find_go_test.go b/pkg/frontend/vcs/source/find_go_test.go index 3079a2c55c..dcc2ee4bc0 100644 --- a/pkg/frontend/vcs/source/find_go_test.go +++ b/pkg/frontend/vcs/source/find_go_test.go @@ -63,6 +63,36 @@ func Test_tryFindGoFile(t *testing.T) { expectedSearchedPaths: []string{"main.go"}, expectedError: nil, }, + { + name: "path with relative repository prefix and major version", + searchedPath: "github.com/grafana/pyroscope/v2/main.go", + rootPath: "", + repo: pyroscopeRepo, + clientMock: newMockVCSClient().addFiles(newFile("main.go")), + attempts: 1, + expectedSearchedPaths: []string{"main.go"}, + expectedError: nil, + }, + { + name: "path with absolute repository prefix and major version", + searchedPath: "/Users/pyroscope/git/github.com/grafana/pyroscope/v2/main.go", + rootPath: "", + repo: pyroscopeRepo, + clientMock: newMockVCSClient().addFiles(newFile("main.go")), + attempts: 1, + expectedSearchedPaths: []string{"main.go"}, + expectedError: nil, + }, + { + name: "path with v1 directory not stripped (legitimate v1/ dir)", + searchedPath: "github.com/grafana/pyroscope/v1/main.go", + rootPath: "", + repo: pyroscopeRepo, + clientMock: newMockVCSClient().addFiles(newFile("v1/main.go")), + attempts: 2, + expectedSearchedPaths: []string{"v1/main.go"}, + expectedError: nil, + }, { name: "not found, attempts exceeded", searchedPath: "/var/service1/src/main.go",