Skip to content

Commit

Permalink
cmd/go: make mod init disallow invalid major version suffixes
Browse files Browse the repository at this point in the history
This CL reuses the SplitPathVersion function from the module package to
detect invalid major version suffixes and return a relevant error
message along with a suggested fix.

Fixes #44052
Fixes #46085

Change-Id: I6c06f31a134e864a1d9b6e00c048ca1c59b4365e
Reviewed-on: https://go-review.googlesource.com/c/go/+/288712
Reviewed-by: Jay Conrod <jayconrod@google.com>
Trust: Jay Conrod <jayconrod@google.com>
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
  • Loading branch information
tpaschalis authored and Jay Conrod committed Aug 17, 2021
1 parent 3848488 commit a2a9a7b
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
60 changes: 60 additions & 0 deletions src/cmd/go/internal/modload/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,13 @@ func CreateModFile(ctx context.Context, modPath string) {
}
}
base.Fatalf("go: %v", err)
} else if _, _, ok := module.SplitPathVersion(modPath); !ok {
if strings.HasPrefix(modPath, "gopkg.in/") {
invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))
base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
}
invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))
base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
}

fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
Expand Down Expand Up @@ -1197,3 +1204,56 @@ const (
func modkey(m module.Version) module.Version {
return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
}

func suggestModulePath(path string) string {
var m string

i := len(path)
for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
i--
}
url := path[:i]
url = strings.TrimSuffix(url, "/v")
url = strings.TrimSuffix(url, "/")

f := func(c rune) bool {
return c > '9' || c < '0'
}
s := strings.FieldsFunc(path[i:], f)
if len(s) > 0 {
m = s[0]
}
m = strings.TrimLeft(m, "0")
if m == "" || m == "1" {
return url + "/v2"
}

return url + "/v" + m
}

func suggestGopkgIn(path string) string {
var m string
i := len(path)
for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) {
i--
}
url := path[:i]
url = strings.TrimSuffix(url, ".v")
url = strings.TrimSuffix(url, "/v")
url = strings.TrimSuffix(url, "/")

f := func(c rune) bool {
return c > '9' || c < '0'
}
s := strings.FieldsFunc(path, f)
if len(s) > 0 {
m = s[0]
}

m = strings.TrimLeft(m, "0")

if m == "" {
return url + ".v1"
}
return url + ".v" + m
}
82 changes: 82 additions & 0 deletions src/cmd/go/testdata/script/mod_init_invalid_major.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
env GO111MODULE=on
env GOFLAGS=-mod=mod

! go mod init example.com/user/repo/v0
stderr '(?s)^go: invalid module path "example.com/user/repo/v0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v02
stderr '(?s)^go: invalid module path "example.com/user/repo/v02": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v023
stderr '(?s)^go: invalid module path "example.com/user/repo/v023": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v23$'

! go mod init example.com/user/repo/v1
stderr '(?s)^go: invalid module path "example.com/user/repo/v1": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v2.0
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v2.1.4
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v3.5
stderr '(?s)^go: invalid module path "example.com/user/repo/v3.5": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v3$'

! go mod init example.com/user/repo/v4.1.4
stderr '(?s)^go: invalid module path "example.com/user/repo/v4.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v4$'

! go mod init example.com/user/repo/v.2.3
stderr '(?s)^go: invalid module path "example.com/user/repo/v.2.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'

! go mod init example.com/user/repo/v.5.3
stderr '(?s)^go: invalid module path "example.com/user/repo/v.5.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v5$'

! go mod init gopkg.in/pkg
stderr '(?s)^go: invalid module path "gopkg.in/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/pkg.v1$'

! go mod init gopkg.in/user/pkg
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg/v0
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v0": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg/v1
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg/v2
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v2": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$'

! go mod init gopkg.in/user/pkg.v
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg.v0.1
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v0.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg.v.1
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg.v01
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v01": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$'

! go mod init gopkg.in/user/pkg.v.2.3
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2.3": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$'

# module paths with a trailing dot are rejected as invalid import paths
! go mod init example.com/user/repo/v2.
stderr '(?s)^go: malformed module path "example.com/user/repo/v2.": trailing dot in path element$'

! go mod init example.com/user/repo/v2..
stderr '(?s)^go: malformed module path "example.com/user/repo/v2..": trailing dot in path element$'

! go mod init gopkg.in/user/pkg.v.2.
stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2.": trailing dot in path element$'

! go mod init gopkg.in/user/pkg.v.2..
stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2..": trailing dot in path element$'

# module paths with spaces are also rejected
! go mod init 'foo bar'
stderr '(?s)^go: malformed module path "foo bar": invalid char '' ''$'

! go mod init 'foo bar baz'
stderr '(?s)^go: malformed module path "foo bar baz": invalid char '' ''$'

0 comments on commit a2a9a7b

Please sign in to comment.