@@ -13,7 +13,6 @@ import (
13
13
"github.com/github/codeql-go/extractor/toolchain"
14
14
"github.com/github/codeql-go/extractor/util"
15
15
"golang.org/x/mod/modfile"
16
- "golang.org/x/mod/semver"
17
16
)
18
17
19
18
// DependencyInstallerMode is an enum describing how dependencies should be installed
@@ -49,69 +48,47 @@ type GoWorkspace struct {
49
48
}
50
49
51
50
// Represents a nullable version string.
52
- type GoVersionInfo struct {
53
- // The version string, if any
54
- Version string
55
- // A value indicating whether a version string was found
56
- Found bool
57
- }
58
-
59
- // Represents a `GoVersionInfo` indicating that no version was found.
60
- var VersionNotFound GoVersionInfo = GoVersionInfo {"" , false }
61
-
62
- // Constructs a `GoVersionInfo` for a version we found.
63
- func VersionFound (version string ) GoVersionInfo {
64
- // Add the "v" required by `semver` if there isn't one yet.
65
- if ! strings .HasPrefix (version , "v" ) {
66
- version = "v" + version
67
- }
68
-
69
- return GoVersionInfo {
70
- Version : version ,
71
- Found : true ,
72
- }
73
- }
51
+ type GoVersionInfo = util.SemVer
74
52
75
53
// Determines the version of Go that is required by this workspace. This is, in order of preference:
76
54
// 1. The Go version specified in the `go.work` file, if any.
77
55
// 2. The greatest Go version specified in any `go.mod` file, if any.
78
- func (workspace * GoWorkspace ) RequiredGoVersion () GoVersionInfo {
56
+ func (workspace * GoWorkspace ) RequiredGoVersion () util. SemVer {
79
57
if workspace .WorkspaceFile != nil && workspace .WorkspaceFile .Go != nil {
80
58
// If we have parsed a `go.work` file, return the version number from it.
81
- return GoVersionInfo { Version : workspace .WorkspaceFile .Go .Version , Found : true }
59
+ return util . NewSemVer ( workspace .WorkspaceFile .Go .Version )
82
60
} else if workspace .Modules != nil && len (workspace .Modules ) > 0 {
83
61
// Otherwise, if we have `go.work` files, find the greatest Go version in those.
84
- var greatestVersion string = ""
62
+ var greatestVersion util. SemVer = nil
85
63
for _ , module := range workspace .Modules {
86
64
if module .Module != nil && module .Module .Go != nil {
87
65
// If we have parsed the file, retrieve the version number we have already obtained.
88
- if greatestVersion == "" || semver .Compare ("v" + module .Module .Go .Version , "v" + greatestVersion ) > 0 {
89
- greatestVersion = module .Module .Go .Version
66
+ modVersion := util .NewSemVer (module .Module .Go .Version )
67
+ if greatestVersion == nil || modVersion .IsNewerThan (greatestVersion ) {
68
+ greatestVersion = modVersion
90
69
}
91
70
} else {
92
71
modVersion := tryReadGoDirective (module .Path )
93
- if modVersion . Found && (greatestVersion == "" || semver . Compare ( "v" + modVersion .Version , "v" + greatestVersion ) > 0 ) {
94
- greatestVersion = modVersion . Version
72
+ if modVersion != nil && (greatestVersion == nil || modVersion .IsNewerThan ( greatestVersion )) {
73
+ greatestVersion = modVersion
95
74
}
96
75
}
97
76
}
98
77
99
78
// If we have found some version, return it.
100
- if greatestVersion != "" {
101
- return VersionFound (greatestVersion )
102
- }
79
+ return greatestVersion
103
80
}
104
81
105
- return VersionNotFound
82
+ return nil
106
83
}
107
84
108
85
// Finds the greatest Go version required by any of the given `workspaces`.
109
86
// Returns a `GoVersionInfo` value with `Found: false` if no version information is available.
110
- func RequiredGoVersion (workspaces * []GoWorkspace ) GoVersionInfo {
111
- greatestGoVersion := VersionNotFound
87
+ func RequiredGoVersion (workspaces * []GoWorkspace ) util. SemVer {
88
+ var greatestGoVersion util. SemVer = nil
112
89
for _ , workspace := range * workspaces {
113
90
goVersionInfo := workspace .RequiredGoVersion ()
114
- if goVersionInfo . Found && (! greatestGoVersion . Found || semver . Compare ( "v" + goVersionInfo .Version , "v" + greatestGoVersion . Version ) > 0 ) {
91
+ if goVersionInfo != nil && (greatestGoVersion == nil || goVersionInfo .IsNewerThan ( greatestGoVersion ) ) {
115
92
greatestGoVersion = goVersionInfo
116
93
}
117
94
}
@@ -198,8 +175,9 @@ var toolchainVersionRe *regexp.Regexp = regexp.MustCompile(`(?m)^([0-9]+\.[0-9]+
198
175
// Returns true if the `go.mod` file specifies a Go language version, that version is `1.21` or greater, and
199
176
// there is no `toolchain` directive, and the Go language version is not a valid toolchain version.
200
177
func hasInvalidToolchainVersion (modFile * modfile.File ) bool {
178
+ v1_21 := util .NewSemVer ("v1.21.0" )
201
179
return modFile .Toolchain == nil && modFile .Go != nil &&
202
- ! toolchainVersionRe .Match ([]byte (modFile .Go .Version )) && semver . Compare ( "v" + modFile .Go .Version , "v1.21.0" ) >= 0
180
+ ! toolchainVersionRe .Match ([]byte (modFile .Go .Version )) && util . NewSemVer ( modFile .Go .Version ). IsAtLeast ( v1_21 )
203
181
}
204
182
205
183
// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
@@ -553,17 +531,15 @@ const (
553
531
554
532
// argsForGoVersion returns the arguments to pass to the Go compiler for the given `ModMode` and
555
533
// Go version
556
- func (m ModMode ) ArgsForGoVersion (version string ) []string {
534
+ func (m ModMode ) ArgsForGoVersion (version util. SemVer ) []string {
557
535
switch m {
558
536
case ModUnset :
559
537
return []string {}
560
538
case ModReadonly :
561
539
return []string {"-mod=readonly" }
562
540
case ModMod :
563
- if ! semver .IsValid (version ) {
564
- log .Fatalf ("Invalid Go semver: '%s'" , version )
565
- }
566
- if semver .Compare (version , "v1.14" ) < 0 {
541
+ v1_14 := util .NewSemVer ("v1.14" )
542
+ if version .IsOlderThan (v1_14 ) {
567
543
return []string {} // -mod=mod is the default behaviour for go <= 1.13, and is not accepted as an argument
568
544
} else {
569
545
return []string {"-mod=mod" }
@@ -590,7 +566,7 @@ func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
590
566
591
567
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
592
568
// The version string is returned in the "1.2.3" format.
593
- func tryReadGoDirective (path string ) GoVersionInfo {
569
+ func tryReadGoDirective (path string ) util. SemVer {
594
570
versionRe := regexp .MustCompile (`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)` )
595
571
goMod , err := os .ReadFile (path )
596
572
if err != nil {
@@ -599,9 +575,9 @@ func tryReadGoDirective(path string) GoVersionInfo {
599
575
matches := versionRe .FindSubmatch (goMod )
600
576
if matches != nil {
601
577
if len (matches ) > 1 {
602
- return VersionFound (string (matches [1 ]))
578
+ return util . NewSemVer (string (matches [1 ]))
603
579
}
604
580
}
605
581
}
606
- return VersionNotFound
582
+ return nil
607
583
}
0 commit comments