Support and honor the "primary" option for Command plugins

This commit is contained in:
Paul Hinze 2022-07-26 13:47:59 -05:00
parent 5170d83c21
commit 7c56c74bb6
No known key found for this signature in database
GPG Key ID: 70B94C31D170FB29
19 changed files with 85 additions and 16 deletions

View File

@ -2,6 +2,7 @@ package configvagrant
import (
"fmt"
"github.com/hashicorp/go-hclog"
sdk "github.com/hashicorp/vagrant-plugin-sdk"
"github.com/hashicorp/vagrant-plugin-sdk/component"
@ -12,8 +13,8 @@ import (
var CommandOptions = []sdk.Option{
sdk.WithComponents(
&Config{},
&Command{},
),
sdk.WithComponent(&Command{}, &component.CommandOptions{Primary: false}),
sdk.WithName("configvagrant"),
}

View File

@ -19,11 +19,14 @@ import (
var CommandOptions = []sdk.Option{
sdk.WithComponents(
// &Provider{},
&command.Command{},
&host.AlwaysTrueHost{},
&communicator.DummyCommunicator{},
&push.Encouragement{},
),
sdk.WithComponent(&command.Command{}, &component.CommandOptions{
// Should keep the plugin out of the default help output
Primary: false,
}),
sdk.WithComponent(&provider.Happy{}, &component.ProviderOptions{
Priority: 100,
}),

View File

@ -2,13 +2,17 @@ package otherplugin
import (
sdk "github.com/hashicorp/vagrant-plugin-sdk"
"github.com/hashicorp/vagrant-plugin-sdk/component"
"github.com/hashicorp/vagrant/builtin/otherplugin/guest"
)
var CommandOptions = []sdk.Option{
sdk.WithComponents(
&Command{},
&guest.AlwaysTrueGuest{},
),
sdk.WithComponent(&Command{}, &component.CommandOptions{
// Hide command from default help output
Primary: false,
}),
sdk.WithName("otherplugin"),
}

View File

@ -22,6 +22,7 @@ type DynamicCommand struct {
help string
parent *DynamicCommand
flags []*component.CommandFlag
primary bool
}
func (c *DynamicCommand) Run(args []string) int {
@ -154,6 +155,10 @@ func (c *DynamicCommand) Flags() component.CommandFlags {
})
}
func (c *DynamicCommand) Primary() bool {
return c.primary
}
func (c *DynamicCommand) fullName() string {
var v string
if c.parent != nil {

View File

@ -224,6 +224,7 @@ func registerCommand(
synopsis: c.Synopsis,
help: c.Help,
flags: flgs,
primary: c.Primary,
}
if parent != nil {
d.parent = parent
@ -399,10 +400,13 @@ func GroupedHelpFunc(f cli.HelpFunc) cli.HelpFunc {
).Row())
d.Append(glint.Text(""))
// Add common commands
// First add hand-picked common commands
helpCommandsSection(d, "Common commands", commonCommands, commands)
// Make our other commands
// Make our list of other commands by
// - skipping common commands we just printed
// - skipping hand-picked hidden commands
// - skipping commands that set CommandOptions.Primary to false
ignoreMap := map[string]struct{}{}
for k := range hiddenCommands {
ignoreMap[k] = struct{}{}
@ -411,6 +415,16 @@ func GroupedHelpFunc(f cli.HelpFunc) cli.HelpFunc {
ignoreMap[k] = struct{}{}
}
for k, cmdFn := range commands {
cmd, err := cmdFn()
if err != nil {
panic(fmt.Sprintf("failed to load %q command: %s", k, err))
}
if pc, ok := cmd.(Primaryable); ok && pc.Primary() == false {
ignoreMap[k] = struct{}{}
}
}
var otherCommands []string
for k := range commands {
if _, ok := ignoreMap[k]; ok {
@ -464,4 +478,8 @@ func helpCommandsSection(
).PaddingLeft(2))
}
type Primaryable interface {
Primary() bool
}
var helpText = map[string][2]string{}

View File

@ -1,14 +1,18 @@
package cli
import (
sdk "github.com/hashicorp/vagrant-plugin-sdk"
"github.com/hashicorp/vagrant/internal/plugin"
"github.com/hashicorp/vagrant-plugin-sdk"
)
type PluginCommand struct {
*baseCommand
}
func (c *PluginCommand) Primary() bool {
return false
}
func (c *PluginCommand) Run(args []string) int {
plugin, ok := plugin.Builtins[args[0]]
if !ok {

View File

@ -99,6 +99,10 @@ func (c *UICommand) Flags() component.CommandFlags {
})
}
func (c *UICommand) Primary() bool {
return true
}
// func (c *UICommand) AutocompleteArgs() complete.Predictor {
// return complete.PredictNothing
// }

View File

@ -34,6 +34,10 @@ func (c *VersionCommand) Flags() component.CommandFlags {
return c.flagSet(0, nil)
}
func (c *VersionCommand) Primary() bool {
return true
}
// func (c *VersionCommand) AutocompleteArgs() complete.Predictor {
// return complete.PredictNothing
// }

View File

@ -589,17 +589,21 @@ func (b *Basis) RunInit() (result *vagrant_server.Job_InitResult, err error) {
for _, c := range cmds {
fn := c.Value.(component.Command).CommandInfoFunc()
// See core.JobCommandProto
raw, err := b.callDynamicFunc(ctx, b.logger, fn,
(*[]*vagrant_plugin_sdk.Command_CommandInfo)(nil),
argmapper.Typed(b.ctx),
)
if err != nil {
return nil, err
}
result.Commands = append(result.Commands,
raw.([]*vagrant_plugin_sdk.Command_CommandInfo)...)
// Primary comes from plugin options so add that to CommandInfo here
cinfos := raw.([]*vagrant_plugin_sdk.Command_CommandInfo)
copts := c.Options.(*component.CommandOptions)
cinfos[0].Primary = copts.Primary
result.Commands = append(result.Commands, cinfos...)
}
return
@ -750,6 +754,7 @@ func (b *Basis) component(
Name: name,
ServerAddr: b.Client().ServerTarget(),
},
Options: c.Options,
hooks: hooks,
mappers: append(b.mappers, c.Mappers...),
plugin: c,

View File

@ -12,6 +12,9 @@ type Component struct {
Value interface{}
Info *vagrant_server.Component
// Options for component type, see PluginInfo.ComponentOptions
Options interface{}
// These fields can be accessed internally
hooks map[string][]*config.Hook
labels map[string]string

View File

@ -12,6 +12,7 @@ var Mappers = []interface{}{
JobCommandProto,
}
// JobCommandProto converts a CommandInfo into its proto equivalent
func JobCommandProto(c *component.CommandInfo) []*vagrant_plugin_sdk.Command_CommandInfo {
return jobCommandProto(c, []string{})
}

View File

@ -179,8 +179,7 @@ module Vagrant
sf_class.plugin_name = plg[:name]
sf_class.type = plg[:type]
result.register(plg[:name].to_sym) do
# TODO(phinze): wire through CommandOptions and return them here
[proc{sf_class}, {}]
[proc{sf_class}, plg[:options]]
end
end
end

View File

@ -436,6 +436,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "hashicorp.vagrant.sdk.PluginInfo.ComponentOptionsMap" do
map :options, :uint32, :message, 1, "google.protobuf.Any"
end
add_message "hashicorp.vagrant.sdk.PluginInfo.CommandOptions" do
optional :primary, :bool, 1
end
add_message "hashicorp.vagrant.sdk.PluginInfo.ProviderOptions" do
optional :priority, :int32, 1
optional :parallel, :bool, 2
@ -499,6 +502,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
optional :synopsis, :string, 3
repeated :flags, :message, 4, "hashicorp.vagrant.sdk.Command.Flag"
repeated :subcommands, :message, 5, "hashicorp.vagrant.sdk.Command.CommandInfo"
optional :primary, :bool, 6
end
add_message "hashicorp.vagrant.sdk.Command.CommandInfoResp" do
optional :command_info, :message, 1, "hashicorp.vagrant.sdk.Command.CommandInfo"
@ -1120,6 +1124,7 @@ module Hashicorp
PluginInfo::ComponentList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ComponentList").msgclass
PluginInfo::Name = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.Name").msgclass
PluginInfo::ComponentOptionsMap = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ComponentOptionsMap").msgclass
PluginInfo::CommandOptions = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.CommandOptions").msgclass
PluginInfo::ProviderOptions = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ProviderOptions").msgclass
PluginInfo::SyncedFolderOptions = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.SyncedFolderOptions").msgclass
PluginManager = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginManager").msgclass

View File

@ -40,6 +40,8 @@ module VagrantPlugins
return {} if plg_opts.nil?
opts = mapper.unany(plg_opts)
case opts
when Hashicorp::Vagrant::Sdk::PluginInfo::CommandOptions
opts.to_h
when Hashicorp::Vagrant::Sdk::PluginInfo::ProviderOptions
opts.to_h
when Hashicorp::Vagrant::Sdk::PluginInfo::SyncedFolderOptions

View File

@ -102,6 +102,7 @@ module VagrantPlugins
synopsis: info.synopsis,
flags: flags,
subcommands: subcommands,
primary: info.primary,
)
end
end

View File

@ -149,12 +149,15 @@ module VagrantPlugins
end
subcommands = get_subcommands(plugin_name, subcommand_names)
opts = Vagrant.plugin("2").local_manager.commands[plugin_name.to_sym].last
SDK::Command::CommandInfo.new(
name: command_name,
help: hlp_msg,
flags: flags,
synopsis: synopsis,
subcommands: subcommands
subcommands: subcommands,
primary: opts[:primary],
)
end

View File

@ -124,8 +124,11 @@ module VagrantPlugins
def _convert_options_to_proto(type, class_or_tuple_with_class_and_options)
case type
when :COMMAND
# _, command_options = class_or_tuple_with_class_and_options
return Google::Protobuf::Empty.new
_, opts = class_or_tuple_with_class_and_options
return SDK::PluginInfo::CommandOptions.new(
# Primary is always set in V2::Plugin.command
primary: opts[:primary],
)
when :COMMUNICATOR
# No options for communicators
return Google::Protobuf::Empty.new

View File

@ -25,14 +25,16 @@ module VagrantPlugins
:help,
:synopsis,
:flags,
:subcommands
:subcommands,
:primary
def initialize(name:, help:, synopsis: nil, subcommands: [])
def initialize(name:, help:, synopsis: nil, subcommands: [], primary:)
@name = name.to_s
@help = help.to_s
@synopsis = synopsis.to_s
@subcommands = Array(subcommands)
@flags = []
@primary = primary
end
def add_flag(**kwargs)

View File

@ -200,6 +200,7 @@ class VagrantPlugins::CommandServe::Type::CommandInfo
synopsis: info.synopsis,
flags: flags,
subcommands: subcommands,
primary: info.primary,
)
end
end
@ -641,6 +642,7 @@ class Hashicorp::Vagrant::Sdk::Command::CommandInfo
name: name,
help: help,
synopsis: synopsis,
primary: primary,
).tap do |c|
flags.each do |f|
c.add_flag(