Skip to content

Commit 1943e1a

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 1943e1a

2 files changed

Lines changed: 72 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: 44 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+
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,46 @@ 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(cmd *cobra.Command, args []string, toComplete string) (
262+
[]string, cobra.ShellCompDirective) {
263+
// We filter strings that the user already passed to --plugins,
264+
// in case the user chains the --plugins flag multiple times,
265+
alreadyEntered, err := cmd.Flags().GetStringSlice(pluginsFlag)
266+
if err != nil {
267+
cobra.CheckErr(err)
268+
}
269+
270+
var comps = make([]string, 0, len(c.plugins))
271+
272+
for pluginKey, p := range c.plugins {
273+
274+
if slices.Contains(alreadyEntered, pluginKey) {
275+
continue
276+
}
277+
278+
// We also omit deprecated plugins from completion
279+
if deprecated, ok := p.(plugin.Deprecated); ok {
280+
if deprecated.DeprecationWarning() != "" {
281+
continue
282+
}
283+
}
284+
285+
// If the plugin provides a description, we show that
286+
// otherwise, we show the default description
287+
desc := ""
288+
if describable, ok := p.(plugin.Describable); ok {
289+
desc = describable.Description()
290+
} else {
291+
desc = getPluginDescription(pluginKey)
292+
}
293+
294+
comps = append(comps, cobra.CompletionWithDesc(pluginKey, desc))
295+
}
296+
297+
return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
298+
}

0 commit comments

Comments
 (0)