Skip to content

Commit 7b138f1

Browse files
committed
improved Go version check
Highlights: - Go version doesn't follow semver style from syntax perspective, so use semver pkg wasn't helpful - re-implemented go version check without using the semver pkg.
1 parent b13b973 commit 7b138f1

File tree

2 files changed

+105
-25
lines changed

2 files changed

+105
-25
lines changed

cmd/go_version_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import "testing"
20+
21+
func TestCheckGoVersion(t *testing.T) {
22+
23+
tests := []struct {
24+
ver string
25+
isInvalid bool
26+
}{
27+
{"go1.8", true},
28+
{"go1.9", true},
29+
{"go1.10", false},
30+
{"go1.10rc", false},
31+
{"go1.10.1", false},
32+
{"go1.11rc2", false},
33+
}
34+
35+
for _, test := range tests {
36+
err := checkGoVersion(test.ver)
37+
if err != nil {
38+
// go error, but the version isn't invalid
39+
if !test.isInvalid {
40+
t.Errorf("Go version check failed valid version '%s' with error '%s'", test.ver, err)
41+
}
42+
} else {
43+
// got no error, but the version is invalid
44+
if test.isInvalid {
45+
t.Errorf("version '%s' is invalid, but got no error", test.ver)
46+
}
47+
}
48+
}
49+
50+
}

cmd/init_project.go

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import (
2323
"os"
2424
"os/exec"
2525
"path/filepath"
26+
"regexp"
27+
"strconv"
2628
"strings"
2729

28-
"github.com/Masterminds/semver"
2930
"github.com/spf13/cobra"
3031
flag "github.com/spf13/pflag"
3132

@@ -63,6 +64,8 @@ kubebuilder init --domain example.org --license apache2 --owner "The Kubernetes
6364
},
6465
}
6566

67+
initCmd.Flags().BoolVar(
68+
&o.skipGoVersionCheck, "skip-go-version-check", false, "if specified, skip checking the Go version")
6669
initCmd.Flags().BoolVar(
6770
&o.dep, "dep", true, "if specified, determines whether dep will be used.")
6871
o.depFlag = initCmd.Flag("dep")
@@ -78,18 +81,21 @@ kubebuilder init --domain example.org --license apache2 --owner "The Kubernetes
7881
}
7982

8083
type projectOptions struct {
81-
prj *project.Project
82-
bp *project.Boilerplate
83-
gopkg *project.GopkgToml
84-
mgr *manager.Cmd
85-
dkr *manager.Dockerfile
86-
dep bool
87-
depFlag *flag.Flag
88-
depArgs []string
84+
prj *project.Project
85+
bp *project.Boilerplate
86+
gopkg *project.GopkgToml
87+
mgr *manager.Cmd
88+
dkr *manager.Dockerfile
89+
dep bool
90+
depFlag *flag.Flag
91+
depArgs []string
92+
skipGoVersionCheck bool
8993
}
9094

9195
func (o *projectOptions) runInit() {
92-
checkGoVersion()
96+
if !o.skipGoVersionCheck {
97+
ensureGoVersionIsCompatible()
98+
}
9399

94100
if !depExists() {
95101
log.Fatalf("Dep is not installed. Follow steps at: https://golang.github.io/dep/docs/installation.html")
@@ -190,30 +196,54 @@ func boilerplateForFlags(f *flag.FlagSet) *project.Boilerplate {
190196
f.StringVar(&b.Owner, "owner", "", "Owner to add to the copyright")
191197
return b
192198
}
193-
func checkGoVersion() {
199+
200+
func ensureGoVersionIsCompatible() {
201+
err := fetchAndCheckGoVersion()
202+
if err != nil {
203+
log.Fatalf("%s. You can skip this check using the --skip-go-version-check flag", err)
204+
}
205+
}
206+
207+
func fetchAndCheckGoVersion() error {
194208
cmd := exec.Command("go", "version")
195209
out, err := cmd.Output()
196210
if err != nil {
197-
log.Fatalf("Could not execute 'go version': %v", err)
211+
return fmt.Errorf("Failed to retrieve 'go version': %v", string(out))
198212
}
199213

200214
split := strings.Split(string(out), " ")
201215
if len(split) < 3 {
202-
log.Fatalf("Invalid go version: %q", string(out))
216+
return fmt.Errorf("Found invalid Go version: %q", string(out))
203217
}
204-
goVersion := strings.TrimPrefix(split[2], "go")
205-
if ver, err := semver.NewVersion(goVersion); err != nil {
206-
if err != nil {
207-
log.Fatalf("Invalid go version %q: %v", goVersion, err)
208-
}
209-
c, err := semver.NewConstraint(">= 1.10")
210-
if err != nil {
211-
log.Fatal("Invalid constraint: %v", err)
212-
}
213-
if !c.Check(ver) {
214-
log.Fatalf("The go version is %v, must be 1.10+", goVersion)
215-
}
218+
goVer := split[2]
219+
if err := checkGoVersion(goVer); err != nil {
220+
return fmt.Errorf("Go version '%s' is incompatible because '%s'", goVer, err)
221+
}
222+
return nil
223+
}
224+
225+
func checkGoVersion(verStr string) error {
226+
goVerRegex := `^go?([0-9]+)\.([0-9]+)([\.0-9A-Za-z\-]+)?$`
227+
m := regexp.MustCompile(goVerRegex).FindStringSubmatch(verStr)
228+
if m == nil {
229+
return fmt.Errorf("invalid version string")
230+
}
231+
232+
major, err := strconv.Atoi(m[1])
233+
if err != nil {
234+
return fmt.Errorf("error parsing major version '%s': %s", m[1], err)
216235
}
236+
237+
minor, err := strconv.Atoi(m[2])
238+
if err != nil {
239+
return fmt.Errorf("error parsing minor version '%s': %s", m[2], err)
240+
}
241+
242+
if major < 1 || minor < 10 {
243+
return fmt.Errorf("requires version >= 1.10")
244+
}
245+
246+
return nil
217247
}
218248

219249
func depExists() bool {

0 commit comments

Comments
 (0)