Skip to content

Commit e1b2bf8

Browse files
authored
Tools.gosupport tools.go for makefile (#64)
* support tools.go * fix backward compatibility * add tests * tests * allow tools file to be defined
1 parent e71bac0 commit e1b2bf8

File tree

7 files changed

+223
-51
lines changed

7 files changed

+223
-51
lines changed

cmd/makefile.go

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,46 @@ package cmd
22

33
import (
44
"errors"
5+
"os"
56

67
"github.com/bakito/toolbox/pkg/makefile"
78
"github.com/go-resty/resty/v2"
89
"github.com/spf13/cobra"
910
)
1011

11-
const flagFile = "file"
12+
const (
13+
flagFile = "file"
14+
flagToolsGo = "tools-go"
15+
)
1216

13-
// makefileCmd represents the makefile command
14-
var makefileCmd = &cobra.Command{
15-
Use: "makefile [tools]",
16-
Short: "Adds tools to a Makefile",
17-
Args: func(_ *cobra.Command, args []string) error {
18-
if len(args) == 0 {
19-
return errors.New("at least one tool must be provided")
20-
}
21-
return nil
22-
},
23-
RunE: func(cmd *cobra.Command, args []string) error {
24-
client := resty.New()
25-
mf, err := cmd.Flags().GetString(flagFile)
26-
if err != nil {
27-
return err
28-
}
29-
return makefile.Generate(client, cmd.OutOrStderr(), mf, args...)
30-
},
31-
}
17+
var (
18+
toolsGo string
19+
// makefileCmd represents the makefile command
20+
makefileCmd = &cobra.Command{
21+
Use: "makefile [tools]",
22+
Short: "Adds tools to a Makefile",
23+
Args: func(_ *cobra.Command, args []string) error {
24+
if _, err := os.Stat(toolsGo); err != nil {
25+
if len(args) == 0 {
26+
return errors.New("at least one tool must be provided")
27+
}
28+
}
29+
return nil
30+
},
31+
RunE: func(cmd *cobra.Command, args []string) error {
32+
client := resty.New()
33+
mf, err := cmd.Flags().GetString(flagFile)
34+
if err != nil {
35+
return err
36+
}
37+
return makefile.Generate(client, cmd.OutOrStderr(), mf, toolsGo, args...)
38+
},
39+
}
40+
)
3241

3342
func init() {
3443
rootCmd.AddCommand(makefileCmd)
3544

3645
makefileCmd.Flags().StringP(flagFile, "f", "", "The Makefile path to generate tools in")
46+
makefileCmd.Flags().StringVar(&toolsGo, flagToolsGo, "tools.go", "The tools.go file to check for tools dependencies.")
3747
}

pkg/makefile/Makefile.tpl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ $(LOCALBIN):
99
{{- range .Tools }}
1010
{{.UpperName}} ?= $(LOCALBIN)/{{.Name}}
1111
{{- end }}
12+
{{- if .WithVersions }}
1213

1314
## Tool Versions
1415
{{- range .Tools }}
16+
{{- if .Version }}
1517
{{.UpperName}}_VERSION ?= {{.Version}}
1618
{{- end }}
19+
{{- end }}
20+
{{- end }}
1721

1822
## Tool Installer
1923
{{- range .Tools }}
2024
.PHONY: {{.Name}}
2125
{{.Name}}: $({{.UpperName}}) ## Download {{.Name}} locally if necessary.
2226
$({{.UpperName}}): $(LOCALBIN)
23-
test -s $(LOCALBIN)/{{.Name}} || GOBIN=$(LOCALBIN) go install {{.ToolName}}@$({{.UpperName}}_VERSION)
27+
test -s $(LOCALBIN)/{{.Name}} || GOBIN=$(LOCALBIN) go install {{.ToolName}}{{- if .Version }}@$({{.UpperName}}_VERSION){{- end }}
2428
{{- end }}
2529

2630
## Update Tools
@@ -29,6 +33,6 @@ update-toolbox-tools:
2933
@rm -f{{- range .Tools }} \
3034
$(LOCALBIN)/{{.Name}}
3135
{{- end }}
32-
toolbox makefile -f $(LOCALDIR)/Makefile{{- range .Tools }} \
33-
{{.Tool}}
36+
toolbox makefile -f $(LOCALDIR)/Makefile{{- range .Tools }}{{- if not .FromToolsGo }} \
37+
{{.Tool}}{{- end }}
3438
{{- end }}

pkg/makefile/make.go

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ import (
1515
)
1616

1717
var (
18-
pattern = regexp.MustCompile(`^github\.com\/([\w-]+\/[\w-]+).*$`)
19-
getRelease = github.LatestRelease
18+
githubPattern = regexp.MustCompile(`^github\.com/([\w-]+/[\w-]+).*$`)
19+
getRelease = github.LatestRelease
2020
)
2121

22-
func Generate(client *resty.Client, writer io.Writer, makefile string, tools ...string) error {
23-
var toolData []toolData
22+
func Generate(client *resty.Client, writer io.Writer, makefile string, toolsFile string, tools ...string) error {
23+
argTools, toolData := mergeWithToolsGo(toolsFile, tools)
24+
return generate(client, writer, makefile, argTools, toolData)
25+
}
2426

25-
for _, t := range tools {
26-
td, err := data(client, t)
27+
func generate(client *resty.Client, writer io.Writer, makefile string, argTools []string, toolData []toolData) error {
28+
for _, t := range argTools {
29+
td, err := dataForArg(client, t)
2730
if err != nil {
2831
return err
2932
}
@@ -34,10 +37,18 @@ func Generate(client *resty.Client, writer io.Writer, makefile string, tools ...
3437
return toolData[i].Name < toolData[j].Name
3538
})
3639

40+
withVersions := false
41+
for _, td := range toolData {
42+
if !withVersions && td.Version != "" {
43+
withVersions = true
44+
}
45+
}
46+
3747
out := &bytes.Buffer{}
3848
t := template.Must(template.New("Makefile").Parse(makefileTemplate))
3949
if err := t.Execute(out, map[string]interface{}{
40-
"Tools": toolData,
50+
"Tools": toolData,
51+
"WithVersions": withVersions,
4152
}); err != nil {
4253
return err
4354
}
@@ -69,37 +80,73 @@ func Generate(client *resty.Client, writer io.Writer, makefile string, tools ...
6980
return os.WriteFile(makefile, []byte(file), 0o600)
7081
}
7182

72-
func data(client *resty.Client, tool string) (toolData, error) {
83+
func dataForArg(client *resty.Client, tool string) (toolData, error) {
7384
toolRepo := strings.Split(tool, "@")
74-
7585
toolName := toolRepo[0]
86+
87+
td := dataForTool(false, toolName, tool)
88+
7689
repo := toolRepo[len(toolRepo)-1]
77-
match := pattern.FindStringSubmatch(repo)
78-
t := toolData{}
90+
match := githubPattern.FindStringSubmatch(repo)
7991

8092
if len(match) != 2 {
81-
return t, fmt.Errorf("invalid tool %q", tool)
93+
return td, fmt.Errorf("invalid tool %q", tool)
8294
}
83-
8495
ghr, err := getRelease(client, match[1], true)
8596
if err != nil {
86-
return t, err
97+
return td, err
8798
}
99+
td.Version = ghr.TagName
88100

89-
parts := strings.Split(toolName, "/")
101+
return td, nil
102+
}
90103

91-
t.Version = ghr.TagName
92-
t.ToolName = toolName
93-
t.Tool = tool
94-
t.Name = parts[len(parts)-1]
95-
t.UpperName = strings.ReplaceAll(strings.ToUpper(t.Name), "-", "_")
96-
return t, nil
104+
func dataForTool(fromToolsGo bool, toolName string, fullTool ...string) (td toolData) {
105+
parts := strings.Split(toolName, "/")
106+
td.ToolName = toolName
107+
if len(fullTool) == 1 {
108+
td.Tool = fullTool[0]
109+
} else {
110+
td.Tool = toolName
111+
}
112+
td.Name = parts[len(parts)-1]
113+
td.UpperName = strings.ReplaceAll(strings.ToUpper(td.Name), "-", "_")
114+
td.FromToolsGo = fromToolsGo
115+
return
97116
}
98117

99118
type toolData struct {
100-
Name string `json:"Name"`
101-
UpperName string `json:"UpperName"`
102-
Version string `json:"Version"`
103-
Tool string `json:"Tool"`
104-
ToolName string `json:"ToolName"`
119+
Name string `json:"Name"`
120+
UpperName string `json:"UpperName"`
121+
Version string `json:"Version"`
122+
Tool string `json:"Tool"`
123+
ToolName string `json:"ToolName"`
124+
FromToolsGo bool `json:"FromToolsGo"`
125+
}
126+
127+
func mergeWithToolsGo(fileName string, inTools []string) ([]string, []toolData) {
128+
content, err := os.ReadFile(fileName)
129+
if err != nil {
130+
return inTools, nil
131+
}
132+
133+
t := make(map[string]bool)
134+
for _, tool := range inTools {
135+
t[tool] = true
136+
}
137+
138+
r := regexp.MustCompile(`"(.*)"`)
139+
var goTools []toolData
140+
for _, m := range r.FindAllStringSubmatch(string(content), -1) {
141+
tool := m[1]
142+
goTools = append(goTools, dataForTool(true, tool))
143+
delete(t, tool)
144+
}
145+
146+
var argTools []string
147+
for t := range t {
148+
argTools = append(argTools, t)
149+
}
150+
151+
return argTools, goTools
105152
}

pkg/makefile/make_test.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var _ = Describe("Make", func() {
3131
Context("Generate", func() {
3232
It("should generate a correct output", func() {
3333
out := &bytes.Buffer{}
34-
err := Generate(resty.New(), out, "",
34+
err := Generate(resty.New(), out, "", "",
3535
"sigs.k8s.io/controller-tools/cmd/[email protected]/kubernetes-sigs/controller-tools",
3636
"github.com/bakito/semver",
3737
"github.com/bakito/toolbox",
@@ -42,7 +42,7 @@ var _ = Describe("Make", func() {
4242
It("should generate a correct output", func() {
4343
out := &bytes.Buffer{}
4444
path := copyFile("Makefile.content", tempDir)
45-
err := Generate(resty.New(), out, path,
45+
err := Generate(resty.New(), out, path, "",
4646
"sigs.k8s.io/controller-tools/cmd/[email protected]/kubernetes-sigs/controller-tools",
4747
"github.com/bakito/semver",
4848
"github.com/bakito/toolbox",
@@ -52,6 +52,31 @@ var _ = Describe("Make", func() {
5252

5353
Ω(readFile(path)).Should(Equal(readFile(testDataDir, "Makefile.content.expected")))
5454
})
55+
It("should generate a correct output", func() {
56+
out := &bytes.Buffer{}
57+
58+
err := Generate(resty.New(), out, "",
59+
filepath.Join(testDataDir, "tools.go.tst"),
60+
"sigs.k8s.io/controller-tools/cmd/[email protected]/kubernetes-sigs/controller-tools",
61+
"github.com/bakito/toolbox",
62+
)
63+
Ω(err).ShouldNot(HaveOccurred())
64+
Ω(out.String() + "\n").Should(Equal(readFile(testDataDir, "Makefile.hybrid.expected")))
65+
})
66+
})
67+
Context("generate", func() {
68+
It("should generate a correct output", func() {
69+
out := &bytes.Buffer{}
70+
71+
td := []toolData{
72+
dataForTool(true, "sigs.k8s.io/controller-tools/cmd/controller-gen"),
73+
dataForTool(true, "github.com/bakito/semver"),
74+
dataForTool(true, "github.com/bakito/toolbox"),
75+
}
76+
err := generate(resty.New(), out, "", nil, td)
77+
Ω(err).ShouldNot(HaveOccurred())
78+
Ω(out.String() + "\n").Should(Equal(readFile(testDataDir, "Makefile.tools.go.expected")))
79+
})
5580
})
5681
})
5782

testdata/Makefile.hybrid.expected

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## toolbox - start
2+
## Current working directory
3+
LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
4+
## Location to install dependencies to
5+
LOCALBIN ?= $(LOCALDIR)/bin
6+
$(LOCALBIN):
7+
mkdir -p $(LOCALBIN)
8+
9+
## Tool Binaries
10+
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
11+
SEMVER ?= $(LOCALBIN)/semver
12+
TOOLBOX ?= $(LOCALBIN)/toolbox
13+
14+
## Tool Versions
15+
CONTROLLER_GEN_VERSION ?= v0.2.1
16+
TOOLBOX_VERSION ?= v0.2.1
17+
18+
## Tool Installer
19+
.PHONY: controller-gen
20+
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
21+
$(CONTROLLER_GEN): $(LOCALBIN)
22+
test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)
23+
.PHONY: semver
24+
semver: $(SEMVER) ## Download semver locally if necessary.
25+
$(SEMVER): $(LOCALBIN)
26+
test -s $(LOCALBIN)/semver || GOBIN=$(LOCALBIN) go install github.com/bakito/semver
27+
.PHONY: toolbox
28+
toolbox: $(TOOLBOX) ## Download toolbox locally if necessary.
29+
$(TOOLBOX): $(LOCALBIN)
30+
test -s $(LOCALBIN)/toolbox || GOBIN=$(LOCALBIN) go install github.com/bakito/toolbox@$(TOOLBOX_VERSION)
31+
32+
## Update Tools
33+
.PHONY: update-toolbox-tools
34+
update-toolbox-tools:
35+
@rm -f \
36+
$(LOCALBIN)/controller-gen \
37+
$(LOCALBIN)/semver \
38+
$(LOCALBIN)/toolbox
39+
toolbox makefile -f $(LOCALDIR)/Makefile \
40+
sigs.k8s.io/controller-tools/cmd/[email protected]/kubernetes-sigs/controller-tools \
41+
github.com/bakito/toolbox
42+
## toolbox - end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## toolbox - start
2+
## Current working directory
3+
LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
4+
## Location to install dependencies to
5+
LOCALBIN ?= $(LOCALDIR)/bin
6+
$(LOCALBIN):
7+
mkdir -p $(LOCALBIN)
8+
9+
## Tool Binaries
10+
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
11+
SEMVER ?= $(LOCALBIN)/semver
12+
TOOLBOX ?= $(LOCALBIN)/toolbox
13+
14+
## Tool Installer
15+
.PHONY: controller-gen
16+
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
17+
$(CONTROLLER_GEN): $(LOCALBIN)
18+
test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen
19+
.PHONY: semver
20+
semver: $(SEMVER) ## Download semver locally if necessary.
21+
$(SEMVER): $(LOCALBIN)
22+
test -s $(LOCALBIN)/semver || GOBIN=$(LOCALBIN) go install github.com/bakito/semver
23+
.PHONY: toolbox
24+
toolbox: $(TOOLBOX) ## Download toolbox locally if necessary.
25+
$(TOOLBOX): $(LOCALBIN)
26+
test -s $(LOCALBIN)/toolbox || GOBIN=$(LOCALBIN) go install github.com/bakito/toolbox
27+
28+
## Update Tools
29+
.PHONY: update-toolbox-tools
30+
update-toolbox-tools:
31+
@rm -f \
32+
$(LOCALBIN)/controller-gen \
33+
$(LOCALBIN)/semver \
34+
$(LOCALBIN)/toolbox
35+
toolbox makefile -f $(LOCALDIR)/Makefile
36+
## toolbox - end

testdata/tools.go.tst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build tools
2+
// +build tools
3+
4+
package tools
5+
6+
import (
7+
_ "github.com/bakito/semver"
8+
)

0 commit comments

Comments
 (0)