Skip to content

Commit

Permalink
fix: improve runtime version parsing (#4961)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored Aug 27, 2024
1 parent 98b685c commit 54d089d
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 178 deletions.
57 changes: 0 additions & 57 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package config

import (
"fmt"
"go/version"
"os"
"regexp"
"runtime"
"strings"

hcversion "github.com/hashicorp/go-version"
Expand Down Expand Up @@ -92,56 +88,3 @@ func detectGoVersion() string {

return "1.17"
}

// Trims the Go version to keep only M.m.
// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0).
// The version can also include information which we want to remove (ex: 1.21alpha1)
// https://go.dev/doc/toolchain#versions
// This a problem with staticcheck and gocritic.
func trimGoVersion(v string) string {
if v == "" {
return ""
}

exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`)

if exp.MatchString(v) {
return exp.FindStringSubmatch(v)[1]
}

return v
}

func getRuntimeGoVersion() string {
goVersion := runtime.Version()

parts := strings.Fields(goVersion)

if len(parts) == 0 {
return goVersion
}

// When using GOEXPERIMENT, the version returned might look something like "go1.23.0 X:boringcrypto".
return parts[0]
}

func checkGoVersion(goVersion string) error {
langVersion := version.Lang(getRuntimeGoVersion())

runtimeVersion, err := hcversion.NewVersion(strings.TrimPrefix(langVersion, "go"))
if err != nil {
return err
}

targetedVersion, err := hcversion.NewVersion(trimGoVersion(goVersion))
if err != nil {
return err
}

if runtimeVersion.LessThan(targetedVersion) {
return fmt.Errorf("the Go language version (%s) used to build golangci-lint is lower than the targeted Go version (%s)",
langVersion, goVersion)
}

return nil
}
102 changes: 0 additions & 102 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIsGoGreaterThanOrEqual(t *testing.T) {
Expand Down Expand Up @@ -84,104 +83,3 @@ func TestIsGoGreaterThanOrEqual(t *testing.T) {
})
}
}

func Test_trimGoVersion(t *testing.T) {
testCases := []struct {
desc string
version string
expected string
}{
{
desc: "patched version",
version: "1.22.0",
expected: "1.22",
},
{
desc: "minor version",
version: "1.22",
expected: "1.22",
},
{
desc: "RC version",
version: "1.22rc1",
expected: "1.22",
},
{
desc: "alpha version",
version: "1.22alpha1",
expected: "1.22",
},
{
desc: "beta version",
version: "1.22beta1",
expected: "1.22",
},
{
desc: "semver RC version",
version: "1.22.0-rc1",
expected: "1.22",
},
}

for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

version := trimGoVersion(test.version)
assert.Equal(t, test.expected, version)
})
}
}

func Test_checkGoVersion(t *testing.T) {
testCases := []struct {
desc string
version string
require require.ErrorAssertionFunc
}{
{
desc: "version greater than runtime version (patch)",
version: "1.30.1",
require: require.Error,
},
{
desc: "version greater than runtime version (family)",
version: "1.30",
require: require.Error,
},
{
desc: "version greater than runtime version (RC)",
version: "1.30.0-rc1",
require: require.Error,
},
{
desc: "version equals to runtime version",
version: getRuntimeGoVersion(),
require: require.NoError,
},
{
desc: "version lower than runtime version (patch)",
version: "1.19.1",
require: require.NoError,
},
{
desc: "version lower than runtime version (family)",
version: "1.19",
require: require.NoError,
},
{
desc: "version lower than runtime version (RC)",
version: "1.19.0-rc1",
require: require.NoError,
},
}

for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

err := checkGoVersion(test.version)
test.require(t, err)
})
}
}
5 changes: 3 additions & 2 deletions pkg/config/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/golangci/golangci-lint/pkg/exitcodes"
"github.com/golangci/golangci-lint/pkg/fsutils"
"github.com/golangci/golangci-lint/pkg/goutil"
"github.com/golangci/golangci-lint/pkg/logutils"
)

Expand Down Expand Up @@ -74,7 +75,7 @@ func (l *Loader) Load(opts LoadOptions) error {

l.handleGoVersion()

err = checkGoVersion(l.cfg.Run.Go)
err = goutil.CheckGoVersion(l.cfg.Run.Go)
if err != nil {
return err
}
Expand Down Expand Up @@ -295,7 +296,7 @@ func (l *Loader) handleGoVersion() {
l.cfg.LintersSettings.Gofumpt.LangVersion = l.cfg.Run.Go
}

trimmedGoVersion := trimGoVersion(l.cfg.Run.Go)
trimmedGoVersion := goutil.TrimGoVersion(l.cfg.Run.Go)

l.cfg.LintersSettings.Revive.Go = trimmedGoVersion

Expand Down
25 changes: 8 additions & 17 deletions pkg/goanalysis/runner_loadingpackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import (
"go/types"
"os"
"reflect"
"runtime"
"strings"
"sync"
"sync/atomic"

"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/go/packages"

"github.com/golangci/golangci-lint/pkg/goanalysis/load"
"github.com/golangci/golangci-lint/pkg/goutil"
"github.com/golangci/golangci-lint/pkg/logutils"
)

Expand Down Expand Up @@ -153,12 +152,18 @@ func (lp *loadingPackage) loadFromSource(loadMode LoadMode) error {
return imp.Types, nil
}

// TODO(ldez) temporary workaround
rv, err := goutil.CleanRuntimeVersion()
if err != nil {
return err
}

tc := &types.Config{
Importer: importerFunc(importer),
Error: func(err error) {
pkg.Errors = append(pkg.Errors, lp.convertError(err)...)
},
GoVersion: getGoVersion(),
GoVersion: rv, // TODO(ldez) temporary workaround
}

_ = types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)
Expand Down Expand Up @@ -502,17 +507,3 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc
panic("unknown rv of type " + rv.String())
}
}

// TODO(ldez) temporary workaround
func getGoVersion() string {
goVersion := runtime.Version()

parts := strings.Fields(goVersion)

if len(parts) == 0 {
return goVersion
}

// When using GOEXPERIMENT, the version returned might look something like "go1.23.0 X:boringcrypto".
return parts[0]
}
75 changes: 75 additions & 0 deletions pkg/goutil/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package goutil

import (
"fmt"
"go/version"
"regexp"
"runtime"
"strings"

hcversion "github.com/hashicorp/go-version"
)

func CheckGoVersion(goVersion string) error {
rv, err := CleanRuntimeVersion()
if err != nil {
return fmt.Errorf("clean runtime version: %w", err)
}

langVersion := version.Lang(rv)

runtimeVersion, err := hcversion.NewVersion(strings.TrimPrefix(langVersion, "go"))
if err != nil {
return err
}

targetedVersion, err := hcversion.NewVersion(TrimGoVersion(goVersion))
if err != nil {
return err
}

if runtimeVersion.LessThan(targetedVersion) {
return fmt.Errorf("the Go language version (%s) used to build golangci-lint is lower than the targeted Go version (%s)",
langVersion, goVersion)
}

return nil
}

// TrimGoVersion Trims the Go version to keep only M.m.
// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0).
// The version can also include information which we want to remove (ex: 1.21alpha1)
// https://go.dev/doc/toolchain#versions
// This a problem with staticcheck and gocritic.
func TrimGoVersion(v string) string {
if v == "" {
return ""
}

exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`)

if exp.MatchString(v) {
return exp.FindStringSubmatch(v)[1]
}

return v
}

func CleanRuntimeVersion() (string, error) {
return cleanRuntimeVersion(runtime.Version())
}

func cleanRuntimeVersion(rv string) (string, error) {
parts := strings.Fields(rv)

for _, part := range parts {
// Allow to handle:
// - GOEXPERIMENT -> "go1.23.0 X:boringcrypto"
// - devel -> "devel go1.24-e705a2d Wed Aug 7 01:16:42 2024 +0000 linux/amd64"
if strings.HasPrefix(part, "go1.") {
return part, nil
}
}

return "", fmt.Errorf("invalid Go runtime version: %s", rv)
}
Loading

0 comments on commit 54d089d

Please sign in to comment.