Skip to content

Commit a3e0baf

Browse files
authored
Merge pull request #1748 from Adirio/plugin-subcommand-concept
⚠️ [Rename] Disambiguate plugin by calling subcommand what the plugin getters returns instead of plugin
2 parents 459b9eb + 7d86f0d commit a3e0baf

21 files changed

+312
-306
lines changed

pkg/cli/api.go

+17-17
Original file line numberDiff line numberDiff line change
@@ -56,38 +56,38 @@ func (c cli) newAPIContext() plugin.Context {
5656

5757
// nolint:dupl
5858
func (c cli) bindCreateAPI(ctx plugin.Context, cmd *cobra.Command) {
59-
var getter plugin.CreateAPIPluginGetter
59+
var createAPIPlugin plugin.CreateAPI
6060
for _, p := range c.resolvedPlugins {
61-
tmpGetter, isGetter := p.(plugin.CreateAPIPluginGetter)
62-
if isGetter {
63-
if getter != nil {
64-
err := fmt.Errorf("duplicate API creation plugins for project version %q (%s, %s), "+
65-
"use a more specific plugin key", c.projectVersion, plugin.KeyFor(getter), plugin.KeyFor(p))
61+
tmpPlugin, isValid := p.(plugin.CreateAPI)
62+
if isValid {
63+
if createAPIPlugin != nil {
64+
err := fmt.Errorf("duplicate API creation plugins (%s, %s), use a more specific plugin key",
65+
plugin.KeyFor(createAPIPlugin), plugin.KeyFor(p))
6666
cmdErr(cmd, err)
6767
return
6868
}
69-
getter = tmpGetter
69+
createAPIPlugin = tmpPlugin
7070
}
7171
}
7272

73-
cfg, err := config.LoadInitialized()
74-
if err != nil {
73+
if createAPIPlugin == nil {
74+
err := fmt.Errorf("relevant plugins do not provide an API creation plugin")
7575
cmdErr(cmd, err)
7676
return
7777
}
7878

79-
if getter == nil {
80-
err := fmt.Errorf("layout plugin %q does not support an API creation plugin", cfg.Layout)
79+
cfg, err := config.LoadInitialized()
80+
if err != nil {
8181
cmdErr(cmd, err)
8282
return
8383
}
8484

85-
createAPI := getter.GetCreateAPIPlugin()
86-
createAPI.InjectConfig(&cfg.Config)
87-
createAPI.BindFlags(cmd.Flags())
88-
createAPI.UpdateContext(&ctx)
85+
subcommand := createAPIPlugin.GetCreateAPISubcommand()
86+
subcommand.InjectConfig(&cfg.Config)
87+
subcommand.BindFlags(cmd.Flags())
88+
subcommand.UpdateContext(&ctx)
8989
cmd.Long = ctx.Description
9090
cmd.Example = ctx.Examples
91-
cmd.RunE = runECmdFunc(cfg, createAPI,
92-
fmt.Sprintf("failed to create API with version %q", c.projectVersion))
91+
cmd.RunE = runECmdFunc(cfg, subcommand,
92+
fmt.Sprintf("failed to create API with %q", plugin.KeyFor(createAPIPlugin)))
9393
}

pkg/cli/cli.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type Option func(*cli) error
5555
// cli defines the command line structure and interfaces that are used to
5656
// scaffold kubebuilder project files.
5757
type cli struct {
58-
// Base command name. Can be injected downstream.
58+
// Root command name. Can be injected downstream.
5959
commandName string
6060
// Default project version. Used in CLI flag setup.
6161
defaultProjectVersion string
@@ -70,16 +70,16 @@ type cli struct {
7070
completionCommand bool
7171

7272
// Plugins injected by options.
73-
pluginsFromOptions map[string][]plugin.Base
73+
pluginsFromOptions map[string][]plugin.Plugin
7474
// Default plugins injected by options. Only one plugin per project version
7575
// is allowed.
76-
defaultPluginsFromOptions map[string]plugin.Base
76+
defaultPluginsFromOptions map[string]plugin.Plugin
7777
// A plugin key passed to --plugins on invoking 'init'.
7878
cliPluginKey string
7979
// A filtered set of plugins that should be used by command constructors.
80-
resolvedPlugins []plugin.Base
80+
resolvedPlugins []plugin.Plugin
8181

82-
// Base command.
82+
// Root command.
8383
cmd *cobra.Command
8484
// Commands injected by options.
8585
extraCommands []*cobra.Command
@@ -90,8 +90,8 @@ func New(opts ...Option) (CLI, error) {
9090
c := &cli{
9191
commandName: "kubebuilder",
9292
defaultProjectVersion: internalconfig.DefaultVersion,
93-
pluginsFromOptions: make(map[string][]plugin.Base),
94-
defaultPluginsFromOptions: make(map[string]plugin.Base),
93+
pluginsFromOptions: make(map[string][]plugin.Plugin),
94+
defaultPluginsFromOptions: make(map[string]plugin.Plugin),
9595
}
9696
for _, opt := range opts {
9797
if err := opt(c); err != nil {
@@ -131,7 +131,7 @@ func WithDefaultProjectVersion(version string) Option {
131131
}
132132

133133
// WithPlugins is an Option that sets the cli's plugins.
134-
func WithPlugins(plugins ...plugin.Base) Option {
134+
func WithPlugins(plugins ...plugin.Plugin) Option {
135135
return func(c *cli) error {
136136
for _, p := range plugins {
137137
for _, version := range p.SupportedProjectVersions() {
@@ -149,7 +149,7 @@ func WithPlugins(plugins ...plugin.Base) Option {
149149

150150
// WithDefaultPlugins is an Option that sets the cli's default plugins. Only
151151
// one plugin per project version is allowed.
152-
func WithDefaultPlugins(plugins ...plugin.Base) Option {
152+
func WithDefaultPlugins(plugins ...plugin.Plugin) Option {
153153
return func(c *cli) error {
154154
for _, p := range plugins {
155155
for _, version := range p.SupportedProjectVersions() {
@@ -230,7 +230,7 @@ func (c *cli) initialize() error {
230230
// in situations like 'init --plugins "go"' when multiple go-type plugins
231231
// are available but only one default is for a particular project version.
232232
allPlugins := c.pluginsFromOptions[c.projectVersion]
233-
defaultPlugin := []plugin.Base{c.defaultPluginsFromOptions[c.projectVersion]}
233+
defaultPlugin := []plugin.Plugin{c.defaultPluginsFromOptions[c.projectVersion]}
234234
switch {
235235
case c.cliPluginKey != "":
236236
// Filter plugin by keys passed in CLI.

pkg/cli/cli_test.go

+43-31
Original file line numberDiff line numberDiff line change
@@ -40,59 +40,71 @@ func (p mockPlugin) Name() string { return p.name }
4040
func (p mockPlugin) Version() plugin.Version { return p.version }
4141
func (p mockPlugin) SupportedProjectVersions() []string { return p.projectVersions }
4242

43-
func (mockPlugin) UpdateContext(*plugin.Context) {}
44-
func (mockPlugin) BindFlags(*pflag.FlagSet) {}
45-
func (mockPlugin) InjectConfig(*config.Config) {}
46-
func (mockPlugin) Run() error { return nil }
47-
48-
func makeBasePlugin(name, version string, projVers ...string) plugin.Base {
43+
func makeBasePlugin(name, version string, projVers ...string) plugin.Plugin {
4944
v, err := plugin.ParseVersion(version)
5045
if err != nil {
5146
panic(err)
5247
}
5348
return mockPlugin{name, v, projVers}
5449
}
5550

56-
func makePluginsForKeys(keys ...string) (plugins []plugin.Base) {
51+
func makePluginsForKeys(keys ...string) (plugins []plugin.Plugin) {
5752
for _, key := range keys {
5853
n, v := plugin.SplitKey(key)
5954
plugins = append(plugins, makeBasePlugin(n, v, internalconfig.DefaultVersion))
6055
}
6156
return
6257
}
6358

59+
type mockSubcommand struct{}
60+
61+
func (mockSubcommand) UpdateContext(*plugin.Context) {}
62+
func (mockSubcommand) BindFlags(*pflag.FlagSet) {}
63+
func (mockSubcommand) InjectConfig(*config.Config) {}
64+
func (mockSubcommand) Run() error { return nil }
65+
66+
// nolint:maligned
6467
type mockAllPlugin struct {
6568
mockPlugin
6669
mockInitPlugin
6770
mockCreateAPIPlugin
6871
mockCreateWebhookPlugin
72+
mockEditPlugin
6973
}
7074

71-
type mockInitPlugin struct{ mockPlugin }
72-
type mockCreateAPIPlugin struct{ mockPlugin }
73-
type mockCreateWebhookPlugin struct{ mockPlugin }
75+
type mockInitPlugin struct{ mockSubcommand }
76+
type mockCreateAPIPlugin struct{ mockSubcommand }
77+
type mockCreateWebhookPlugin struct{ mockSubcommand }
78+
type mockEditPlugin struct{ mockSubcommand }
7479

75-
// GetInitPlugin will return the plugin which is responsible for initialized the project
76-
func (p mockInitPlugin) GetInitPlugin() plugin.Init { return p }
80+
// GetInitSubcommand implements plugin.Init
81+
func (p mockInitPlugin) GetInitSubcommand() plugin.InitSubcommand { return p }
7782

78-
// GetCreateAPIPlugin will return the plugin which is responsible for scaffolding APIs for the project
79-
func (p mockCreateAPIPlugin) GetCreateAPIPlugin() plugin.CreateAPI { return p }
83+
// GetCreateAPISubcommand implements plugin.CreateAPI
84+
func (p mockCreateAPIPlugin) GetCreateAPISubcommand() plugin.CreateAPISubcommand { return p }
85+
86+
// GetCreateWebhookSubcommand implements plugin.CreateWebhook
87+
func (p mockCreateWebhookPlugin) GetCreateWebhookSubcommand() plugin.CreateWebhookSubcommand {
88+
return p
89+
}
8090

81-
// GetCreateWebhookPlugin will return the plugin which is responsible for scaffolding webhooks for the project
82-
func (p mockCreateWebhookPlugin) GetCreateWebhookPlugin() plugin.CreateWebhook { return p }
91+
// GetEditSubcommand implements plugin.Edit
92+
func (p mockEditPlugin) GetEditSubcommand() plugin.EditSubcommand { return p }
8393

84-
func makeAllPlugin(name, version string, projectVersions ...string) plugin.Base {
94+
func makeAllPlugin(name, version string, projectVersions ...string) plugin.Plugin {
8595
p := makeBasePlugin(name, version, projectVersions...).(mockPlugin)
96+
subcommand := mockSubcommand{}
8697
return mockAllPlugin{
8798
p,
88-
mockInitPlugin{p},
89-
mockCreateAPIPlugin{p},
90-
mockCreateWebhookPlugin{p},
99+
mockInitPlugin{subcommand},
100+
mockCreateAPIPlugin{subcommand},
101+
mockCreateWebhookPlugin{subcommand},
102+
mockEditPlugin{subcommand},
91103
}
92104
}
93105

94-
func makeSetByProjVer(ps ...plugin.Base) map[string][]plugin.Base {
95-
set := make(map[string][]plugin.Base)
106+
func makeSetByProjVer(ps ...plugin.Plugin) map[string][]plugin.Plugin {
107+
set := make(map[string][]plugin.Plugin)
96108
for _, p := range ps {
97109
for _, version := range p.SupportedProjectVersions() {
98110
set[version] = append(set[version], p)
@@ -117,7 +129,7 @@ var _ = Describe("CLI", func() {
117129
pluginAV2 = makeAllPlugin(pluginNameA, "v2", projectVersions...)
118130
pluginBV1 = makeAllPlugin(pluginNameB, "v1", projectVersions...)
119131
pluginBV2 = makeAllPlugin(pluginNameB, "v2", projectVersions...)
120-
allPlugins = []plugin.Base{pluginAV1, pluginAV2, pluginBV1, pluginBV2}
132+
allPlugins = []plugin.Plugin{pluginAV1, pluginAV2, pluginBV1, pluginBV2}
121133
)
122134

123135
Describe("New", func() {
@@ -129,28 +141,28 @@ var _ = Describe("CLI", func() {
129141
Expect(err).NotTo(HaveOccurred())
130142
Expect(c).NotTo(BeNil())
131143
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1)))
132-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
144+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
133145

134146
By("setting two plugins with different names and versions")
135147
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(pluginAV1, pluginBV2))
136148
Expect(err).NotTo(HaveOccurred())
137149
Expect(c).NotTo(BeNil())
138150
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginBV2)))
139-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
151+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
140152

141153
By("setting two plugins with the same names and different versions")
142154
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(pluginAV1, pluginAV2))
143155
Expect(err).NotTo(HaveOccurred())
144156
Expect(c).NotTo(BeNil())
145157
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginAV2)))
146-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
158+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
147159

148160
By("setting two plugins with different names and the same version")
149161
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(pluginAV1, pluginBV1))
150162
Expect(err).NotTo(HaveOccurred())
151163
Expect(c).NotTo(BeNil())
152164
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginBV1)))
153-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
165+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
154166
})
155167

156168
It("should return an error", func() {
@@ -193,31 +205,31 @@ var _ = Describe("CLI", func() {
193205
Expect(err).NotTo(HaveOccurred())
194206
Expect(c).NotTo(BeNil())
195207
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginAV2)))
196-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
208+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
197209

198210
By(`setting cliPluginKey to "go/v1"`)
199211
setPluginsFlag("go/v1")
200212
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(pluginAV1, pluginBV2))
201213
Expect(err).NotTo(HaveOccurred())
202214
Expect(c).NotTo(BeNil())
203215
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginBV2)))
204-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginAV1}))
216+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginAV1}))
205217

206218
By(`setting cliPluginKey to "go/v2"`)
207219
setPluginsFlag("go/v2")
208220
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(pluginAV1, pluginBV2))
209221
Expect(err).NotTo(HaveOccurred())
210222
Expect(c).NotTo(BeNil())
211223
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(pluginAV1, pluginBV2)))
212-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginBV2}))
224+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginBV2}))
213225

214226
By(`setting cliPluginKey to "go.test.com/v2"`)
215227
setPluginsFlag("go.test.com/v2")
216228
c, err = New(WithDefaultPlugins(pluginAV1), WithPlugins(allPlugins...))
217229
Expect(err).NotTo(HaveOccurred())
218230
Expect(c).NotTo(BeNil())
219231
Expect(c.(*cli).pluginsFromOptions).To(Equal(makeSetByProjVer(allPlugins...)))
220-
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Base{pluginBV2}))
232+
Expect(c.(*cli).resolvedPlugins).To(Equal([]plugin.Plugin{pluginBV2}))
221233
})
222234

223235
It("should return an error", func() {

pkg/cli/cmd_helpers.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,15 @@ func errCmdFunc(err error) func(*cobra.Command, []string) error {
4545
}
4646
}
4747

48-
// runECmdFunc returns a cobra RunE function that runs gsub and saves the
49-
// config, which may have been modified by gsub.
48+
// runECmdFunc returns a cobra RunE function that runs subcommand and saves the
49+
// config, which may have been modified by subcommand.
5050
func runECmdFunc(
5151
c *config.Config,
52-
gsub plugin.GenericSubcommand, // nolint:interfacer
53-
msg string) func(*cobra.Command, []string) error {
52+
subcommand plugin.Subcommand, // nolint:interfacer
53+
msg string,
54+
) func(*cobra.Command, []string) error {
5455
return func(*cobra.Command, []string) error {
55-
if err := gsub.Run(); err != nil {
56+
if err := subcommand.Run(); err != nil {
5657
return fmt.Errorf("%s: %v", msg, err)
5758
}
5859
return c.Save()

pkg/cli/edit.go

+20-19
Original file line numberDiff line numberDiff line change
@@ -46,47 +46,48 @@ func (c *cli) newEditCmd() *cobra.Command {
4646
func (c *cli) newEditContext() plugin.Context {
4747
ctx := plugin.Context{
4848
CommandName: c.commandName,
49-
Description: `This command will edit the project configuration. You can have single or multi group project.`,
49+
Description: `Edit the project configuration.
50+
`,
5051
}
5152

5253
return ctx
5354
}
5455

5556
// nolint:dupl
5657
func (c *cli) bindEdit(ctx plugin.Context, cmd *cobra.Command) {
57-
var getter plugin.EditPluginGetter
58+
var editPlugin plugin.Edit
5859
for _, p := range c.resolvedPlugins {
59-
tmpGetter, isGetter := p.(plugin.EditPluginGetter)
60-
if isGetter {
61-
if getter != nil {
62-
err := fmt.Errorf("duplicate edit project plugins for project version %q (%s, %s), "+
63-
"use a more specific plugin key", c.projectVersion, plugin.KeyFor(getter), plugin.KeyFor(p))
60+
tmpPlugin, isValid := p.(plugin.Edit)
61+
if isValid {
62+
if editPlugin != nil {
63+
err := fmt.Errorf(
64+
"duplicate edit project plugins (%s, %s), use a more specific plugin key",
65+
plugin.KeyFor(editPlugin), plugin.KeyFor(p))
6466
cmdErr(cmd, err)
6567
return
6668
}
67-
getter = tmpGetter
69+
editPlugin = tmpPlugin
6870
}
6971
}
7072

71-
cfg, err := config.LoadInitialized()
72-
if err != nil {
73+
if editPlugin == nil {
74+
err := fmt.Errorf("relevant plugins do not provide a project edit plugin")
7375
cmdErr(cmd, err)
7476
return
7577
}
7678

77-
if getter == nil {
78-
err := fmt.Errorf("layout plugin %q does not support a edit project plugin", cfg.Layout)
79+
cfg, err := config.LoadInitialized()
80+
if err != nil {
7981
cmdErr(cmd, err)
8082
return
8183
}
8284

83-
editProject := getter.GetEditPlugin()
84-
editProject.InjectConfig(&cfg.Config)
85-
editProject.BindFlags(cmd.Flags())
86-
editProject.UpdateContext(&ctx)
85+
subcommand := editPlugin.GetEditSubcommand()
86+
subcommand.InjectConfig(&cfg.Config)
87+
subcommand.BindFlags(cmd.Flags())
88+
subcommand.UpdateContext(&ctx)
8789
cmd.Long = ctx.Description
8890
cmd.Example = ctx.Examples
89-
cmd.RunE = runECmdFunc(cfg, editProject,
90-
fmt.Sprintf("failed to edit project with version %q", c.projectVersion))
91-
91+
cmd.RunE = runECmdFunc(cfg, subcommand,
92+
fmt.Sprintf("failed to edit project with %q", plugin.KeyFor(editPlugin)))
9293
}

0 commit comments

Comments
 (0)