Skip to content

Commit

Permalink
Go: Use SemVer type in project package
Browse files Browse the repository at this point in the history
  • Loading branch information
mbg committed May 8, 2024
1 parent 1b82143 commit 646b3e3
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 41 deletions.
2 changes: 1 addition & 1 deletion go/extractor/autobuilder/build-environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func IdentifyEnvironment() {

// Find the greatest Go version required by any of the workspaces.
greatestGoVersion := project.RequiredGoVersion(&workspaces)
v.goModVersion, v.goModVersionFound = greatestGoVersion.Version, greatestGoVersion.Found
v.goModVersion, v.goModVersionFound = greatestGoVersion.String(), greatestGoVersion != nil

// Find which, if any, version of Go is installed on the system already.
v.goEnvVersionFound = toolchain.IsInstalled()
Expand Down
1 change: 0 additions & 1 deletion go/extractor/cli/go-autobuilder/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions go/extractor/cli/go-autobuilder/go-autobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"runtime"
"strings"

"golang.org/x/mod/semver"

"github.com/github/codeql-go/extractor/autobuilder"
"github.com/github/codeql-go/extractor/diagnostics"
"github.com/github/codeql-go/extractor/project"
Expand Down Expand Up @@ -334,7 +332,7 @@ func buildWithoutCustomCommands(modMode project.ModMode) bool {
log.Println("Build failed, continuing to install dependencies.")

shouldInstallDependencies = true
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer().String())...) {
} else if toolchain.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...) {
log.Println("Dependencies are still not resolving after the build, continuing to install dependencies.")

shouldInstallDependencies = true
Expand Down Expand Up @@ -448,7 +446,7 @@ func extract(workspace project.GoWorkspace) bool {

extractorArgs := []string{}
if workspace.DepMode == project.GoGetWithModules {
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer().String())...)
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...)
}

if len(workspace.Modules) == 0 {
Expand Down Expand Up @@ -539,12 +537,12 @@ func installDependenciesAndBuild() {
// This diagnostic is not required if the system Go version is 1.21 or greater, since the
// Go tooling should install required Go versions as needed.
v1_21 := util.NewSemVer("v1.21.0")
if toolchain.GetEnvGoSemVer().IsOlderThan(v1_21) && greatestGoVersion.Found && semver.Compare("v"+greatestGoVersion.Version, toolchain.GetEnvGoSemVer().String()) > 0 {
diagnostics.EmitNewerGoVersionNeeded(toolchain.GetEnvGoSemVer().String(), "v"+greatestGoVersion.Version)
if toolchain.GetEnvGoSemVer().IsOlderThan(v1_21) && greatestGoVersion != nil && greatestGoVersion.IsNewerThan(toolchain.GetEnvGoSemVer()) {
diagnostics.EmitNewerGoVersionNeeded(toolchain.GetEnvGoSemVer().String(), greatestGoVersion.String())
if val, _ := os.LookupEnv("GITHUB_ACTIONS"); val == "true" {
log.Printf(
"A go.mod file requires version %s of Go, but version %s is installed. Consider adding an actions/setup-go step to your workflow.\n",
"v"+greatestGoVersion.Version,
greatestGoVersion,
toolchain.GetEnvGoSemVer())
}
}
Expand All @@ -556,7 +554,7 @@ func installDependenciesAndBuild() {
for i, workspace := range workspaces {
goVersionInfo := workspace.RequiredGoVersion()

fixGoVendorIssues(&workspace, goVersionInfo.Found)
fixGoVendorIssues(&workspace, goVersionInfo != nil)

tryUpdateGoModAndGoSum(workspace)

Expand Down
1 change: 0 additions & 1 deletion go/extractor/project/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 22 additions & 30 deletions go/extractor/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/github/codeql-go/extractor/toolchain"
"github.com/github/codeql-go/extractor/util"
"golang.org/x/mod/modfile"
"golang.org/x/mod/semver"
)

// DependencyInstallerMode is an enum describing how dependencies should be installed
Expand Down Expand Up @@ -49,53 +48,47 @@ type GoWorkspace struct {
}

// Represents a nullable version string.
type GoVersionInfo struct {
// The version string, if any
Version string
// A value indicating whether a version string was found
Found bool
}
type GoVersionInfo = util.SemVer

// Determines the version of Go that is required by this workspace. This is, in order of preference:
// 1. The Go version specified in the `go.work` file, if any.
// 2. The greatest Go version specified in any `go.mod` file, if any.
func (workspace *GoWorkspace) RequiredGoVersion() GoVersionInfo {
func (workspace *GoWorkspace) RequiredGoVersion() util.SemVer {
if workspace.WorkspaceFile != nil && workspace.WorkspaceFile.Go != nil {
// If we have parsed a `go.work` file, return the version number from it.
return GoVersionInfo{Version: workspace.WorkspaceFile.Go.Version, Found: true}
return util.NewSemVer(workspace.WorkspaceFile.Go.Version)
} else if workspace.Modules != nil && len(workspace.Modules) > 0 {
// Otherwise, if we have `go.work` files, find the greatest Go version in those.
var greatestVersion string = ""
var greatestVersion util.SemVer = nil
for _, module := range workspace.Modules {
if module.Module != nil && module.Module.Go != nil {
// If we have parsed the file, retrieve the version number we have already obtained.
if greatestVersion == "" || semver.Compare("v"+module.Module.Go.Version, "v"+greatestVersion) > 0 {
greatestVersion = module.Module.Go.Version
modVersion := util.NewSemVer(module.Module.Go.Version)
if greatestVersion == nil || modVersion.IsNewerThan(greatestVersion) {
greatestVersion = modVersion
}
} else {
modVersion := tryReadGoDirective(module.Path)
if modVersion.Found && (greatestVersion == "" || semver.Compare("v"+modVersion.Version, "v"+greatestVersion) > 0) {
greatestVersion = modVersion.Version
if modVersion != nil && (greatestVersion == nil || modVersion.IsNewerThan(greatestVersion)) {
greatestVersion = modVersion
}
}
}

// If we have found some version, return it.
if greatestVersion != "" {
return GoVersionInfo{Version: greatestVersion, Found: true}
}
return greatestVersion
}

return GoVersionInfo{Version: "", Found: false}
return nil
}

// Finds the greatest Go version required by any of the given `workspaces`.
// Returns a `GoVersionInfo` value with `Found: false` if no version information is available.
func RequiredGoVersion(workspaces *[]GoWorkspace) GoVersionInfo {
greatestGoVersion := GoVersionInfo{Version: "", Found: false}
func RequiredGoVersion(workspaces *[]GoWorkspace) util.SemVer {
var greatestGoVersion util.SemVer = nil
for _, workspace := range *workspaces {
goVersionInfo := workspace.RequiredGoVersion()
if goVersionInfo.Found && (!greatestGoVersion.Found || semver.Compare("v"+goVersionInfo.Version, "v"+greatestGoVersion.Version) > 0) {
if goVersionInfo != nil && (greatestGoVersion == nil || goVersionInfo.IsNewerThan(greatestGoVersion)) {
greatestGoVersion = goVersionInfo
}
}
Expand Down Expand Up @@ -182,8 +175,9 @@ var toolchainVersionRe *regexp.Regexp = regexp.MustCompile(`(?m)^([0-9]+\.[0-9]+
// Returns true if the `go.mod` file specifies a Go language version, that version is `1.21` or greater, and
// there is no `toolchain` directive, and the Go language version is not a valid toolchain version.
func hasInvalidToolchainVersion(modFile *modfile.File) bool {
v1_21 := util.NewSemVer("v1.21.0")
return modFile.Toolchain == nil && modFile.Go != nil &&
!toolchainVersionRe.Match([]byte(modFile.Go.Version)) && semver.Compare("v"+modFile.Go.Version, "v1.21.0") >= 0
!toolchainVersionRe.Match([]byte(modFile.Go.Version)) && util.NewSemVer(modFile.Go.Version).IsAtLeast(v1_21)
}

// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
Expand Down Expand Up @@ -537,17 +531,15 @@ const (

// argsForGoVersion returns the arguments to pass to the Go compiler for the given `ModMode` and
// Go version
func (m ModMode) ArgsForGoVersion(version string) []string {
func (m ModMode) ArgsForGoVersion(version util.SemVer) []string {
switch m {
case ModUnset:
return []string{}
case ModReadonly:
return []string{"-mod=readonly"}
case ModMod:
if !semver.IsValid(version) {
log.Fatalf("Invalid Go semver: '%s'", version)
}
if semver.Compare(version, "v1.14") < 0 {
v1_14 := util.NewSemVer("v1.14")
if version.IsOlderThan(v1_14) {
return []string{} // -mod=mod is the default behaviour for go <= 1.13, and is not accepted as an argument
} else {
return []string{"-mod=mod"}
Expand All @@ -574,7 +566,7 @@ func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {

// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
// The version string is returned in the "1.2.3" format.
func tryReadGoDirective(path string) GoVersionInfo {
func tryReadGoDirective(path string) util.SemVer {
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)`)
goMod, err := os.ReadFile(path)
if err != nil {
Expand All @@ -583,9 +575,9 @@ func tryReadGoDirective(path string) GoVersionInfo {
matches := versionRe.FindSubmatch(goMod)
if matches != nil {
if len(matches) > 1 {
return GoVersionInfo{string(matches[1]), true}
return util.NewSemVer(string(matches[1]))
}
}
}
return GoVersionInfo{"", false}
return nil
}

0 comments on commit 646b3e3

Please sign in to comment.