Skip to content

Commit 7bd7d72

Browse files
scopkangasta
andauthored
fix: various shell flag argument completions taking fixed set of values (#376)
Co-authored-by: Toni Kangas <toni.kangas@upcloud.com>
1 parent 2089117 commit 7bd7d72

File tree

9 files changed

+81
-4
lines changed

9 files changed

+81
-4
lines changed

internal/commands/command.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ func (s *BaseCommand) AddFlags(flags *pflag.FlagSet) {
192192
s.Cobra().Flags().AddFlag(flag)
193193
if _, ok := flag.Annotations[FlagAnnotationNoFileCompletions]; ok {
194194
Must(s.Cobra().RegisterFlagCompletionFunc(flag.Name, cobra.NoFileCompletions))
195+
} else if values, ok := flag.Annotations[FlagAnnotationFixedCompletions]; ok {
196+
Must(s.Cobra().RegisterFlagCompletionFunc(flag.Name, cobra.FixedCompletions(values, cobra.ShellCompDirectiveNoFileComp)))
195197
}
196198
})
197199
}

internal/commands/ipaddress/assign.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func (s *assignCommand) InitCommand() {
5656
config.AddToggleFlag(fs, &s.floating, "floating", false, "Whether the address to be assigned is a floating one.")
5757

5858
s.AddFlags(fs)
59+
commands.Must(s.Cobra().RegisterFlagCompletionFunc("access", cobra.FixedCompletions([]string{"public", "utility"}, cobra.ShellCompDirectiveNoFileComp)))
60+
commands.Must(s.Cobra().RegisterFlagCompletionFunc("family", cobra.FixedCompletions([]string{"IPv4", "IPv6"}, cobra.ShellCompDirectiveNoFileComp)))
5961
commands.Must(s.Cobra().RegisterFlagCompletionFunc("mac", cobra.NoFileCompletions))
6062
}
6163

internal/commands/network/network.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func handleNetwork(in string) (*upcloud.IPNetwork, error) {
5454
return nil, err
5555
}
5656

57+
commands.Must(fs.SetAnnotation("dhcp-dns", commands.FlagAnnotationFixedCompletions, []string{"true", "false"}))
5758
commands.Must(fs.SetAnnotation("address", commands.FlagAnnotationNoFileCompletions, nil))
5859

5960
if dhcp != "" {

internal/commands/servergroup/create.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,16 @@ func (c *createCommand) InitCommand() {
8888
c.params = createParams{CreateServerGroupRequest: request.CreateServerGroupRequest{}}
8989

9090
fs.StringVar(&c.params.Title, "title", defaultCreateParams.Title, "Server group title.")
91-
fs.StringVar(&c.params.antiAffinityPolicy, "anti-affinity-policy", defaultCreateParams.antiAffinityPolicy, "Anti-affinity policy. Valid values are `yes` (best effort), `strict` and `no`. Will take effect upon server start.")
91+
aaPolicies := []string{"yes", "strict", "no"}
92+
fs.StringVar(&c.params.antiAffinityPolicy, "anti-affinity-policy", defaultCreateParams.antiAffinityPolicy, "Anti-affinity policy. Valid values are "+namedargs.ValidValuesHelp(aaPolicies...)+". Will take effect upon server start.")
9293
fs.StringArrayVar(&c.params.labels, "label", defaultCreateParams.labels, "Labels to describe the server group in `key=value` format, multiple can be declared.\nUsage: --label env=dev\n\n--label owner=operations")
9394
fs.StringArrayVar(&c.params.servers, "server", defaultCreateParams.servers, "Servers to be added to the server group, multiple can be declared.\nUsage: --server my-server\n\n--server 00333d1b-3a4a-4b75-820a-4a56d70395dd")
9495

9596
c.AddFlags(fs)
9697

9798
commands.Must(c.Cobra().MarkFlagRequired("title"))
9899
commands.Must(c.Cobra().RegisterFlagCompletionFunc("title", cobra.NoFileCompletions))
100+
commands.Must(c.Cobra().RegisterFlagCompletionFunc("anti-affinity-policy", cobra.FixedCompletions(aaPolicies, cobra.ShellCompDirectiveNoFileComp)))
99101
commands.Must(c.Cobra().RegisterFlagCompletionFunc("label", cobra.NoFileCompletions))
100102

101103
// Deprecating servergroup in favour of server-group

internal/commands/servergroup/modify.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,15 @@ func (p *modifyParams) processParams(exec commands.Executor, uuid string) error
8181
// InitCommand implements Command.InitCommand
8282
func (c *modifyCommand) InitCommand() {
8383
fs := &pflag.FlagSet{}
84-
fs.StringVar(&c.params.antiAffinityPolicy, "anti-affinity-policy", defaultModifyParams.antiAffinityPolicy, "Anti-affinity policy. Valid values are `yes` (best effort), `strict` and `no`. Will take effect upon server start.")
84+
aaPolicies := []string{"yes", "strict", "no"}
85+
fs.StringVar(&c.params.antiAffinityPolicy, "anti-affinity-policy", defaultModifyParams.antiAffinityPolicy, "Anti-affinity policy. Valid values are "+namedargs.ValidValuesHelp(aaPolicies...)+". Will take effect upon server start.")
8586
fs.StringArrayVar(&c.params.labels, "label", defaultModifyParams.labels, "Labels to describe the server in `key=value` format, multiple can be declared. If set, all the existing labels will be replaced with provided ones.\nUsage: --label env=dev\n\n--label owner=operations")
8687
fs.StringVar(&c.params.Title, "title", defaultModifyParams.Title, "New server group title.")
8788
fs.StringArrayVar(&c.params.servers, "server", defaultModifyParams.servers, "Servers that belong to the server group, multiple can be declared. If set, all the existing server entries will be replaced with provided ones.\nUsage: --server my-server\n\n--server 00333d1b-3a4a-4b75-820a-4a56d70395dd")
8889

8990
c.AddFlags(fs)
9091
commands.Must(c.Cobra().RegisterFlagCompletionFunc("title", cobra.NoFileCompletions))
92+
commands.Must(c.Cobra().RegisterFlagCompletionFunc("anti-affinity-policy", cobra.FixedCompletions(aaPolicies, cobra.ShellCompDirectiveNoFileComp)))
9193
commands.Must(c.Cobra().RegisterFlagCompletionFunc("label", cobra.NoFileCompletions))
9294

9395
// Deprecating servergroup in favour of server-group

internal/commands/util.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
const (
2222
// FlagAnnotationNoFileCompletions is the annotation name to use for our flags that have no filename completions.
2323
FlagAnnotationNoFileCompletions = "upctl_flag_no_file_completions"
24+
// FlagAnnotationFixedCompletions is the annotation name to use for our flags that have a fixed set of completions.
25+
FlagAnnotationFixedCompletions = "upctl_flag_fixed_completions"
2426
)
2527

2628
// ParseN parses a complex, querystring-type argument from `in` and splits values to `n` amount of substrings

internal/core/core.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
99
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands/all"
1010
"github.com/UpCloudLtd/upcloud-cli/v3/internal/config"
11+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/namedargs"
1112
"github.com/UpCloudLtd/upcloud-cli/v3/internal/terminal"
1213
"github.com/UpCloudLtd/upcloud-cli/v3/internal/ui"
1314

@@ -90,9 +91,10 @@ func BuildRootCmd(conf *config.Config) cobra.Command {
9091
flags.StringVarP(
9192
&conf.GlobalFlags.ConfigFile, "config", "", "", "Configuration file path.",
9293
)
94+
outputFormats := []string{config.ValueOutputHuman, config.ValueOutputJSON, config.ValueOutputYAML}
9395
flags.StringVarP(
9496
&conf.GlobalFlags.OutputFormat, "output", "o", "human",
95-
"Output format (supported: json, yaml and human)",
97+
"Output format. Valid values are "+namedargs.ValidValuesHelp(outputFormats...)+".",
9698
)
9799
config.AddToggleFlag(flags, &conf.GlobalFlags.ForceColours, "force-colours", false, "Force coloured output despite detected terminal support.")
98100
config.AddToggleFlag(flags, &conf.GlobalFlags.NoColours, "no-colours", false, "Disable coloured output despite detected terminal support. Colours can also be disabled by setting NO_COLOR environment variable.")
@@ -116,6 +118,7 @@ func BuildRootCmd(conf *config.Config) cobra.Command {
116118
rootCmd.SetUsageTemplate(ui.CommandUsageTemplate())
117119
rootCmd.SetUsageFunc(ui.UsageFunc)
118120

121+
commands.Must(rootCmd.RegisterFlagCompletionFunc("output", cobra.FixedCompletions(outputFormats, cobra.ShellCompDirectiveNoFileComp)))
119122
commands.Must(rootCmd.RegisterFlagCompletionFunc("client-timeout", cobra.NoFileCompletions))
120123

121124
return rootCmd

internal/namedargs/text.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
11
package namedargs
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
"strings"
6+
)
47

58
// Returns description for --zone argument, e.g. "Zone where to create the resource...".
69
func ZoneDescription(resource string) string {
710
return fmt.Sprintf("Zone where to create the %s. Run `upctl zone list` to list all available zones.", resource)
811
}
12+
13+
// ValidValuesHelp wraps values in backticks and adds human readable separators.
14+
// For example, "`one`, `two` and `three`".
15+
func ValidValuesHelp(values ...string) string {
16+
if len(values) == 0 {
17+
return ""
18+
}
19+
20+
if len(values) == 1 {
21+
return fmt.Sprintf("`%s`", values[0])
22+
}
23+
24+
return fmt.Sprintf(
25+
"`%s` and `%s`",
26+
strings.Join(values[:len(values)-1], "`, `"),
27+
values[len(values)-1],
28+
)
29+
}

internal/namedargs/text_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package namedargs_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/namedargs"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestValidValuesHelp(t *testing.T) {
11+
for _, test := range []struct {
12+
name string
13+
values []string
14+
expected string
15+
}{
16+
{
17+
name: "no values",
18+
values: []string{},
19+
expected: "",
20+
},
21+
{
22+
name: "single values",
23+
values: []string{"foo"},
24+
expected: "`foo`",
25+
},
26+
{
27+
name: "two values",
28+
values: []string{"foo", "bar"},
29+
expected: "`foo` and `bar`",
30+
},
31+
{
32+
name: "multiple values",
33+
values: []string{"foo", "bar", "baz"},
34+
expected: "`foo`, `bar` and `baz`",
35+
},
36+
} {
37+
t.Run(test.name, func(t *testing.T) {
38+
actual := namedargs.ValidValuesHelp(test.values...)
39+
assert.Equal(t, test.expected, actual)
40+
})
41+
}
42+
}

0 commit comments

Comments
 (0)