Skip to content

Commit 5176cd0

Browse files
authored
Merge pull request #121 from commitdev/117-add-check-command
Added command to check your local installs.
2 parents f146c26 + 0be16e7 commit 5176cd0

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

cmd/check.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"regexp"
8+
"strings"
9+
"text/tabwriter"
10+
11+
"github.com/coreos/go-semver/semver"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func init() {
16+
rootCmd.AddCommand(checkCmd)
17+
}
18+
19+
type requirement struct {
20+
name string
21+
command string
22+
args []string
23+
minVersion string
24+
regexStr string
25+
docsURL string
26+
}
27+
28+
type versionError struct {
29+
errorText string
30+
}
31+
32+
type commandError struct {
33+
Command string
34+
ErrorText string
35+
Suggestion string
36+
}
37+
38+
func (e *versionError) Error() string {
39+
return fmt.Sprintf("%s", e.errorText)
40+
}
41+
42+
func (e *commandError) Error() string {
43+
return fmt.Sprintf("%s", e.ErrorText)
44+
}
45+
46+
func printErrors(errors []commandError) {
47+
// initialize tabwriter
48+
w := new(tabwriter.Writer)
49+
50+
// minwidth, tabwidth, padding, padchar, flags
51+
w.Init(os.Stdout, 10, 12, 2, ' ', 0)
52+
53+
defer w.Flush()
54+
55+
fmt.Fprintf(w, "\n %s\t%s\t%s\t", "Command", "Error", "Info")
56+
fmt.Fprintf(w, "\n %s\t%s\t%s\t", "---------", "---------", "---------")
57+
58+
for _, e := range errors {
59+
fmt.Fprintf(w, "\n%s\t%s\t%s\t", e.Command, e.ErrorText, e.Suggestion)
60+
}
61+
}
62+
63+
// getSemver uses the regular expression from the requirement to parse the
64+
// output of a command and extract the version from it. Returns the version
65+
// or an error if the version string could not be parsed.
66+
func getSemver(req requirement, out []byte) (*semver.Version, error) {
67+
re := regexp.MustCompile(req.regexStr)
68+
v := re.FindStringSubmatch(string(out))
69+
if len(v) < 4 {
70+
return nil, &commandError{
71+
req.command,
72+
"Could not find version number in output",
73+
fmt.Sprintf("Try running %s %s locally and checking it works.", req.command, strings.Join(req.args, " ")),
74+
}
75+
}
76+
versionString := fmt.Sprintf("%s.%s.%s", v[1], v[2], v[3])
77+
version, err := semver.NewVersion(versionString)
78+
if err != nil {
79+
return version, err
80+
}
81+
return version, nil
82+
}
83+
84+
// checkSemver validates that the version of a tool meets the minimum required
85+
// version listed in your requirement. Returns a boolean.
86+
// For more information on parsing semver, see semver.org
87+
// If your tool doesn't do full semver then you may need to add custom logic
88+
// to support it.
89+
func checkSemver(req requirement, actualVersion *semver.Version) bool {
90+
requiredVersion := semver.New(req.minVersion)
91+
return actualVersion.LessThan(*requiredVersion)
92+
}
93+
94+
var checkCmd = &cobra.Command{
95+
Use: "check",
96+
Short: "Print the check number of commit0",
97+
Run: func(cmd *cobra.Command, args []string) {
98+
// Add any new requirements to this slice.
99+
required := []requirement{
100+
{
101+
name: "AWS CLI\t\t",
102+
command: "aws",
103+
args: []string{"--version"},
104+
regexStr: `aws-cli\/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)`,
105+
minVersion: "1.16.0",
106+
docsURL: "https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html",
107+
},
108+
{
109+
name: "Kubectl\t\t",
110+
command: "kubectl",
111+
args: []string{"version", "--client=true", "--short"},
112+
regexStr: `Client Version: v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)`,
113+
minVersion: "1.15.2",
114+
docsURL: "https://kubernetes.io/docs/tasks/tools/install-kubectl/",
115+
},
116+
{
117+
name: "Terraform\t",
118+
command: "terraform",
119+
args: []string{"version"},
120+
regexStr: `Terraform v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)`,
121+
minVersion: "0.12.0",
122+
docsURL: "https://www.terraform.io/downloads.html",
123+
},
124+
{
125+
name: "jq\t\t",
126+
command: "jq",
127+
args: []string{"--version"},
128+
regexStr: `jq-(0|[1-9]\d*)\.(0|[1-9]\d*)-(0|[1-9]\d*)`,
129+
minVersion: "1.5.0",
130+
docsURL: "https://stedolan.github.io/jq/download/",
131+
},
132+
{
133+
name: "Git\t\t",
134+
command: "git",
135+
args: []string{"version"},
136+
regexStr: `^git version (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)`,
137+
minVersion: "2.17.1",
138+
docsURL: "https://git-scm.com/book/en/v2/Getting-Started-Installing-Git",
139+
},
140+
}
141+
142+
// Store and errors from the commands we run.
143+
errors := []commandError{}
144+
145+
fmt.Println("Checking Zero Requirements...")
146+
for _, r := range required {
147+
fmt.Printf("%s", r.name)
148+
// In future we could parse the stderr and stdout separately, but for now it's nice to see
149+
// the full output on a failure.
150+
out, err := exec.Command(r.command, r.args...).CombinedOutput()
151+
if err != nil {
152+
cerr := commandError{
153+
fmt.Sprintf("%s %s", r.command, strings.Join(r.args, " ")),
154+
err.Error(),
155+
r.docsURL,
156+
}
157+
errors = append(errors, cerr)
158+
fmt.Printf("\033[0;31mFAIL\033[0m\t\t%s\n", "-")
159+
continue
160+
}
161+
version, err := getSemver(r, out)
162+
if err != nil {
163+
cerr := commandError{
164+
r.command,
165+
err.Error(),
166+
r.docsURL,
167+
}
168+
errors = append(errors, cerr)
169+
fmt.Printf("\033[0;31mFAIL\033[0m\t\t%s\n", version)
170+
continue
171+
}
172+
if checkSemver(r, version) {
173+
cerr := commandError{
174+
r.command,
175+
fmt.Sprintf("Version does not meet required. Want: %s; Got: %s", r.minVersion, version),
176+
r.docsURL,
177+
}
178+
errors = append(errors, cerr)
179+
fmt.Printf("\033[0;31mFAIL\033[0m\t\t%s\n", version)
180+
} else {
181+
fmt.Printf("\033[0;32mPASS\033[0m\t\t%s\n", version)
182+
}
183+
}
184+
185+
if len(errors) > 0 {
186+
printErrors(errors)
187+
os.Exit(1)
188+
}
189+
190+
fmt.Println()
191+
},
192+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/chzyer/logex v1.1.10 // indirect
88
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
99
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
10+
github.com/coreos/go-semver v0.2.0
1011
github.com/google/uuid v1.1.1
1112
github.com/gorilla/handlers v1.4.2
1213
github.com/gorilla/mux v1.7.3

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
2626
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
2727
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
2828
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
29+
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
2930
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
3031
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
3132
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

0 commit comments

Comments
 (0)