From a2a9a7b5132c7a0b1d4c297018d6072101456709 Mon Sep 17 00:00:00 2001 From: Paschalis Tsilias Date: Tue, 2 Feb 2021 15:25:21 +0200 Subject: [PATCH] cmd/go: make mod init disallow invalid major version suffixes 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 Trust: Jay Conrod Trust: Bryan C. Mills Run-TryBot: Jay Conrod TryBot-Result: Go Bot --- src/cmd/go/internal/modload/init.go | 60 ++++++++++++++ .../script/mod_init_invalid_major.txt | 82 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/cmd/go/testdata/script/mod_init_invalid_major.txt diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index d5f9d10422..862b007739 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -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) @@ -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 +} diff --git a/src/cmd/go/testdata/script/mod_init_invalid_major.txt b/src/cmd/go/testdata/script/mod_init_invalid_major.txt new file mode 100644 index 0000000000..ae93e70d63 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_init_invalid_major.txt @@ -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 '' ''$' -- 2.48.1