Skip to content

Commit c6c1805

Browse files
committed
feat(cli): add completion for the plugins flag
The --plugins flag now shows completion for shows all the available plugins (external ones too!).
1 parent 5dead90 commit c6c1805

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

pkg/cli/cli_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,34 @@ var _ = Describe("CLI", func() {
231231
})
232232
})
233233

234+
Describe("completionPluginsFlag", func() {
235+
It("should suggest available plugins and filter out entered/deprecated ones", func() {
236+
p1 := newMockPlugin("p1.io", "v1", projectVersion)
237+
p2 := newMockPlugin("p2.io", "v1", projectVersion)
238+
p3 := newMockDeprecatedPlugin("p3.io", "v1-alpha", "deprecated", projectVersion)
239+
240+
c.plugins = makeMapFor(p1, p2, p3)
241+
242+
k1 := plugin.KeyFor(p1)
243+
k2 := plugin.KeyFor(p2)
244+
245+
c.cmd = c.newRootCmd()
246+
c.cmd.Flags().StringSlice(pluginsFlag, []string{}, "test usage")
247+
248+
err := c.cmd.Flags().Set(pluginsFlag, k1)
249+
Expect(err).NotTo(HaveOccurred())
250+
251+
got, directive := c.completionPluginsFlag(c.cmd, []string{}, "")
252+
253+
Expect(directive).To(Equal(cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace))
254+
255+
expected := fmt.Sprintf("%s\tExternal or custom plugin", k2)
256+
257+
Expect(got).To(HaveLen(1))
258+
Expect(got).To(ContainElement(expected))
259+
})
260+
})
261+
234262
Context("buildCmd", func() {
235263
var projectFile string
236264

pkg/cli/root.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func (c CLI) newRootCmd() *cobra.Command {
103103

104104
// Global flags for all subcommands.
105105
cmd.PersistentFlags().StringSlice(pluginsFlag, nil, "plugin keys to be used for this subcommand execution")
106+
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(pluginsFlag, c.completionPluginsFlag))
106107

107108
// Register --project-version on the root command so that it shows up in help.
108109
cmd.Flags().String(projectVersionFlag, c.defaultProjectVersion.String(), "project version")
@@ -252,3 +253,48 @@ func (c CLI) getPluginTableFilteredWithOptions(filter func(plugin.Plugin) bool,
252253

253254
return strings.Join(lines, "\n")
254255
}
256+
257+
// completePluginsFlag implements cobra.CompletionFunc and is registered
258+
// as the flag completion function for --plugins
259+
// We should note that flag completion does not work for comma-chained values
260+
// but works fine when repeating the flag
261+
func (c CLI) completionPluginsFlag(
262+
cmd *cobra.Command,
263+
_ []string,
264+
_ string,
265+
) ([]string, cobra.ShellCompDirective) {
266+
// We filter strings that the user already passed to --plugins,
267+
// in case the user chains the --plugins flag multiple times,
268+
alreadyEntered, err := cmd.Flags().GetStringSlice(pluginsFlag)
269+
if err != nil {
270+
cobra.CheckErr(err)
271+
}
272+
273+
comps := make([]string, 0, len(c.plugins))
274+
275+
for pluginKey, p := range c.plugins {
276+
if slices.Contains(alreadyEntered, pluginKey) {
277+
continue
278+
}
279+
280+
// We also omit deprecated plugins from completion
281+
if deprecated, ok := p.(plugin.Deprecated); ok {
282+
if deprecated.DeprecationWarning() != "" {
283+
continue
284+
}
285+
}
286+
287+
// If the plugin provides a description, we show that
288+
// otherwise, we show the default description
289+
desc := ""
290+
if describable, ok := p.(plugin.Describable); ok {
291+
desc = describable.Description()
292+
} else {
293+
desc = getPluginDescription(pluginKey)
294+
}
295+
296+
comps = append(comps, cobra.CompletionWithDesc(pluginKey, desc))
297+
}
298+
299+
return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
300+
}

0 commit comments

Comments
 (0)