feat(go): parse go.mod file for version

This commit is contained in:
Jan De Dobbeleer 2021-12-13 22:35:42 +01:00 committed by Jan De Dobbeleer
parent 84e3bea1ea
commit af112277a8
6 changed files with 87 additions and 4 deletions

View file

@ -34,6 +34,7 @@ Display the currently active golang version.
- `files`: the segment is only displayed when `*.go` or `go.mod` files are present (default)
- template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
properties below. Defaults to `{{ .Full }}`
- parse_mod_file: `boolean`: parse the go.mod file instead of calling `go version`
## Template Properties

View file

@ -35,7 +35,10 @@ require (
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect
)
require gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
require (
golang.org/x/mod v0.5.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
@ -55,6 +58,7 @@ require (
github.com/spf13/cast v1.4.1 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -141,18 +141,23 @@ github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUA
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -179,6 +184,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=

View file

@ -1,9 +1,17 @@
package main
import (
"golang.org/x/mod/modfile"
)
type golang struct {
language
}
const (
ParseModFile Property = "parse_mod_file"
)
func (g *golang) string() string {
return g.language.string()
}
@ -14,6 +22,10 @@ func (g *golang) init(props properties, env environmentInfo) {
props: props,
extensions: []string{"*.go", "go.mod"},
commands: []*cmd{
{
regex: `(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+)(.(?P<patch>[0-9]+))?))`,
getVersion: g.getVersion,
},
{
executable: "go",
args: []string{"version"},
@ -24,6 +36,22 @@ func (g *golang) init(props properties, env environmentInfo) {
}
}
func (g *golang) getVersion() (string, error) {
if !g.props.getBool(ParseModFile, false) {
return "", nil
}
gomod, err := g.language.env.hasParentFilePath("go.mod")
if err != nil {
return "", nil
}
contents := g.language.env.getFileContent(gomod.path)
file, err := modfile.Parse(gomod.path, []byte(contents), nil)
if err != nil {
return "", err
}
return file.Go.Version, nil
}
func (g *golang) enabled() bool {
return g.language.enabled()
}

View file

@ -1,7 +1,9 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"testing"
"github.com/stretchr/testify/assert"
@ -29,12 +31,25 @@ func getMockedLanguageEnv(params *mockedLanguageParams) (*MockedEnvironment, pro
func TestGolang(t *testing.T) {
cases := []struct {
Case string
ExpectedString string
Version string
Case string
ExpectedString string
Version string
ParseModFile bool
HasModFileInParentDir bool
InvalidModfile bool
}{
{Case: "Go 1.15", ExpectedString: "1.15.8", Version: "go version go1.15.8 darwin/amd64"},
{Case: "Go 1.16", ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
{Case: "go.mod 1.17", ParseModFile: true, HasModFileInParentDir: true, ExpectedString: "1.17"},
{Case: "no go.mod file fallback", ParseModFile: true, ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
{
Case: "invalid go.mod file fallback",
ParseModFile: true,
HasModFileInParentDir: true,
InvalidModfile: true,
ExpectedString: "./go.mod:1: unknown directive: invalid",
Version: "go version go1.16 darwin/amd64",
},
}
for _, tc := range cases {
params := &mockedLanguageParams{
@ -44,6 +59,27 @@ func TestGolang(t *testing.T) {
extension: "*.go",
}
env, props := getMockedLanguageEnv(params)
if tc.ParseModFile {
props[ParseModFile] = tc.ParseModFile
fileInfo := &fileInfo{
path: "./go.mod",
parentFolder: "./",
isDir: false,
}
var err error
if !tc.HasModFileInParentDir {
err = errors.New("no match")
}
env.On("hasParentFilePath", "go.mod").Return(fileInfo, err)
var content string
if tc.InvalidModfile {
content = "invalid go.mod file"
} else {
tmp, _ := ioutil.ReadFile(fileInfo.path)
content = string(tmp)
}
env.On("getFileContent", fileInfo.path).Return(content)
}
g := &golang{}
g.init(props, env)
assert.True(t, g.enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))

View file

@ -640,6 +640,12 @@
"fetch_version": {
"$ref": "#/definitions/fetch_version"
},
"parse_mod_file": {
"type": "boolean",
"title": "Parse go.mod file",
"description": "Parse go.mod file instead of calling out to go to improve performance.",
"default": false
},
"display_mode": {
"$ref": "#/definitions/display_mode"
},