Skip to content

Commit

Permalink
Support major module version paths in imports during save (robfig#47)
Browse files Browse the repository at this point in the history
When an import that contains a (possible) major module version fails,
it is reattempted without the version component.

For example, imports like "sigs.k8s.io/structured-merge-diff/v4/value"
and "k8s.io/klog/v2" will fail because on disk, they reside in
GOPATH/src/sigs.k8s.io/structured-merge-diff/value & GOPATH/src/k8s.io/klog,
respectively.
  • Loading branch information
atavakoliyext authored Sep 3, 2020
1 parent ed09935 commit 58065e5
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 3 deletions.
32 changes: 29 additions & 3 deletions save.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,32 @@ func (p byImportPath) Len() int { return len(p) }
func (p byImportPath) Less(i, j int) bool { return p[i].root < p[j].root }
func (p byImportPath) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

var majorVersionComponent = regexp.MustCompile(`v[\d]+`)

// pathWithoutMajorVersion returns path with the 1st major version /component
// (if any) stripped out. If one was found, the 2nd return value is true.
func pathWithoutMajorVersion(path string) (string, bool) {
parts := strings.Split(path, "/")
for idx, part := range strings.Split(path, "/") {
if majorVersionComponent.MatchString(part) {
return strings.Join(append(parts[:idx], parts[idx+1:]...), "/"), true
}
}
return path, false
}

// tryImport attempts to import the path as-is and, if it fails to be found and
// path contains a major module version, reattempts with the version removed.
func tryImport(ctx build.Context, path, srcDir string, mode build.ImportMode) (*build.Package, error) {
pkg, err := ctx.Import(path, srcDir, mode)
if err != nil && strings.HasPrefix(err.Error(), "cannot find package ") {
if versionlessPath, ok := pathWithoutMajorVersion(path); ok {
return ctx.Import(versionlessPath, srcDir, mode)
}
}
return pkg, err
}

// getAllDeps returns a slice of package import paths for all dependencies
// (including test dependencies) of the given import path (and subpackages) and commands.
func getAllDeps(importPath string, cmds []string) []string {
Expand Down Expand Up @@ -169,7 +195,7 @@ func getAllDeps(importPath string, cmds []string) []string {

// Add the subpackages.
for path := range buildutil.ExpandPatterns(&buildContext, []string{subpackagePrefix + "..."}) {
_, err := buildContext.Import(path, "", 0)
_, err := tryImport(buildContext, path, "", 0)
if _, ok := err.(*build.NoGoError); ok {
continue
}
Expand All @@ -179,7 +205,7 @@ func getAllDeps(importPath string, cmds []string) []string {

var addTransitiveClosure func(string)
addTransitiveClosure = func(path string) {
pkg, err := buildContext.Import(path, "", 0)
pkg, err := tryImport(buildContext, path, "", 0)
printLoadingError(path, err)

importPaths := append([]string(nil), pkg.Imports...)
Expand All @@ -194,7 +220,7 @@ func getAllDeps(importPath string, cmds []string) []string {
}

// Resolve the import path relative to the importing package.
if bp2, _ := buildContext.Import(path, pkg.Dir, build.FindOnly); bp2 != nil {
if bp2, _ := tryImport(buildContext, path, pkg.Dir, build.FindOnly); bp2 != nil {
path = bp2.ImportPath
}

Expand Down
56 changes: 56 additions & 0 deletions save_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,39 @@ var saveTests = []saveTest{
"github.com/test/p2",
},
},

{
"module major versions",
[]pkg{{
"github.com/test/p1",
[]file{
{"foo.go", false, []string{"github.com/test/p2/v2"}},
{"foo_test.go", false, []string{"github.com/test/p3/v2"}},
}}, {
"github.com/test/p2",
[]file{
{"foo.go", false, []string{"github.com/test/p4/v2/subpkg"}},
}}, {
"github.com/test/p3",
[]file{
{"v2/foo.go", false, []string{"os"}},
}}, {
"github.com/test/p4",
[]file{
{"subpkg/foo.go", false, []string{"github.com/test/p5"}},
}}, {
"github.com/test/p5",
[]file{
{"foo.go", false, []string{"os"}},
}},
},
[]string{
"github.com/test/p2",
"github.com/test/p3",
"github.com/test/p4",
"github.com/test/p5",
},
},
}

func TestSave(t *testing.T) {
Expand Down Expand Up @@ -315,3 +348,26 @@ import (
}
}
}

func TestPathWithoutMajorVersion(t *testing.T) {
tests := []struct{
path string
expectedPath string
expectedBool bool
}{
{"github.com/p1", "github.com/p1", false},
{"github.com/p1/v1", "github.com/p1", true},
{"github.com/p2/p3", "github.com/p2/p3", false},
{"github.com/p1/v2/p3", "github.com/p1/p3", true},
}

for _, test := range tests {
actualPath, actualBool := pathWithoutMajorVersion(test.path)
if actualPath != test.expectedPath {
t.Errorf("%v: expected: %v got: %v", test.path, test.expectedPath, actualPath)
}
if actualBool != test.expectedBool {
t.Errorf("%v: expected: %v got: %v", test.path, test.expectedBool, actualBool)
}
}
}

0 comments on commit 58065e5

Please sign in to comment.