mirror of
https://github.com/spf13/cobra
synced 2024-12-25 22:07:05 +00:00
Add env variable to suppress completion descriptions on create (#1938)
COBRA_COMPLETION_DESCRIPTIONS=0 or <PROGRAM>_COMPLETION_DESCRIPTIONS=0 can now be used to disable shell completion descriptions.
This commit is contained in:
parent
236f3c0418
commit
e63925d321
4 changed files with 237 additions and 11 deletions
|
@ -17,21 +17,17 @@ package cobra
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
activeHelpMarker = "_activeHelp_ "
|
||||
// The below values should not be changed: programs will be using them explicitly
|
||||
// in their user documentation, and users will be using them explicitly.
|
||||
activeHelpEnvVarSuffix = "_ACTIVE_HELP"
|
||||
activeHelpGlobalEnvVar = "COBRA_ACTIVE_HELP"
|
||||
activeHelpEnvVarSuffix = "ACTIVE_HELP"
|
||||
activeHelpGlobalEnvVar = configEnvVarGlobalPrefix + "_" + activeHelpEnvVarSuffix
|
||||
activeHelpGlobalDisable = "0"
|
||||
)
|
||||
|
||||
var activeHelpEnvVarPrefixSubstRegexp = regexp.MustCompile(`[^A-Z0-9_]`)
|
||||
|
||||
// AppendActiveHelp adds the specified string to the specified array to be used as ActiveHelp.
|
||||
// Such strings will be processed by the completion script and will be shown as ActiveHelp
|
||||
// to the user.
|
||||
|
@ -60,8 +56,5 @@ func GetActiveHelpConfig(cmd *Command) string {
|
|||
// variable. It has the format <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the
|
||||
// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
func activeHelpEnvVar(name string) string {
|
||||
// This format should not be changed: users will be using it explicitly.
|
||||
activeHelpEnvVar := strings.ToUpper(fmt.Sprintf("%s%s", name, activeHelpEnvVarSuffix))
|
||||
activeHelpEnvVar = activeHelpEnvVarPrefixSubstRegexp.ReplaceAllString(activeHelpEnvVar, "_")
|
||||
return activeHelpEnvVar
|
||||
return configEnvVar(name, activeHelpEnvVarSuffix)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ package cobra
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -211,7 +213,12 @@ func (c *Command) initCompleteCmd(args []string) {
|
|||
// 2- Even without completions, we need to print the directive
|
||||
}
|
||||
|
||||
noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd)
|
||||
noDescriptions := cmd.CalledAs() == ShellCompNoDescRequestCmd
|
||||
if !noDescriptions {
|
||||
if doDescriptions, err := strconv.ParseBool(getEnvConfig(cmd, configEnvVarSuffixDescriptions)); err == nil {
|
||||
noDescriptions = !doDescriptions
|
||||
}
|
||||
}
|
||||
noActiveHelp := GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable
|
||||
out := finalCmd.OutOrStdout()
|
||||
for _, comp := range completions {
|
||||
|
@ -899,3 +906,34 @@ func CompError(msg string) {
|
|||
func CompErrorln(msg string) {
|
||||
CompError(fmt.Sprintf("%s\n", msg))
|
||||
}
|
||||
|
||||
// These values should not be changed: users will be using them explicitly.
|
||||
const (
|
||||
configEnvVarGlobalPrefix = "COBRA"
|
||||
configEnvVarSuffixDescriptions = "COMPLETION_DESCRIPTIONS"
|
||||
)
|
||||
|
||||
var configEnvVarPrefixSubstRegexp = regexp.MustCompile(`[^A-Z0-9_]`)
|
||||
|
||||
// configEnvVar returns the name of the program-specific configuration environment
|
||||
// variable. It has the format <PROGRAM>_<SUFFIX> where <PROGRAM> is the name of the
|
||||
// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
func configEnvVar(name, suffix string) string {
|
||||
// This format should not be changed: users will be using it explicitly.
|
||||
v := strings.ToUpper(fmt.Sprintf("%s_%s", name, suffix))
|
||||
v = configEnvVarPrefixSubstRegexp.ReplaceAllString(v, "_")
|
||||
return v
|
||||
}
|
||||
|
||||
// getEnvConfig returns the value of the configuration environment variable
|
||||
// <PROGRAM>_<SUFFIX> where <PROGRAM> is the name of the root command in upper
|
||||
// case, with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
// If the value is empty or not set, the value of the environment variable
|
||||
// COBRA_<SUFFIX> is returned instead.
|
||||
func getEnvConfig(cmd *Command, suffix string) string {
|
||||
v := os.Getenv(configEnvVar(cmd.Root().Name(), suffix))
|
||||
if v == "" {
|
||||
v = os.Getenv(configEnvVar(configEnvVarGlobalPrefix, suffix))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
@ -3517,3 +3518,194 @@ func TestGetFlagCompletion(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnvConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
use string
|
||||
suffix string
|
||||
cmdVar string
|
||||
globalVar string
|
||||
cmdVal string
|
||||
globalVal string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Command envvar overrides global",
|
||||
use: "root",
|
||||
suffix: "test",
|
||||
cmdVar: "ROOT_TEST",
|
||||
globalVar: "COBRA_TEST",
|
||||
cmdVal: "cmd",
|
||||
globalVal: "global",
|
||||
expected: "cmd",
|
||||
},
|
||||
{
|
||||
desc: "Missing/empty command envvar falls back to global",
|
||||
use: "root",
|
||||
suffix: "test",
|
||||
cmdVar: "ROOT_TEST",
|
||||
globalVar: "COBRA_TEST",
|
||||
cmdVal: "",
|
||||
globalVal: "global",
|
||||
expected: "global",
|
||||
},
|
||||
{
|
||||
desc: "Missing/empty command and global envvars fall back to empty",
|
||||
use: "root",
|
||||
suffix: "test",
|
||||
cmdVar: "ROOT_TEST",
|
||||
globalVar: "COBRA_TEST",
|
||||
cmdVal: "",
|
||||
globalVal: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "Periods in command use transform to underscores in env var name",
|
||||
use: "foo.bar",
|
||||
suffix: "test",
|
||||
cmdVar: "FOO_BAR_TEST",
|
||||
globalVar: "COBRA_TEST",
|
||||
cmdVal: "cmd",
|
||||
globalVal: "global",
|
||||
expected: "cmd",
|
||||
},
|
||||
{
|
||||
desc: "Dashes in command use transform to underscores in env var name",
|
||||
use: "quux-BAZ",
|
||||
suffix: "test",
|
||||
cmdVar: "QUUX_BAZ_TEST",
|
||||
globalVar: "COBRA_TEST",
|
||||
cmdVal: "cmd",
|
||||
globalVal: "global",
|
||||
expected: "cmd",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// Could make env handling cleaner with t.Setenv with Go >= 1.17
|
||||
err := os.Setenv(tc.cmdVar, tc.cmdVal)
|
||||
defer func() {
|
||||
assertNoErr(t, os.Unsetenv(tc.cmdVar))
|
||||
}()
|
||||
assertNoErr(t, err)
|
||||
err = os.Setenv(tc.globalVar, tc.globalVal)
|
||||
defer func() {
|
||||
assertNoErr(t, os.Unsetenv(tc.globalVar))
|
||||
}()
|
||||
assertNoErr(t, err)
|
||||
cmd := &Command{Use: tc.use}
|
||||
got := getEnvConfig(cmd, tc.suffix)
|
||||
if got != tc.expected {
|
||||
t.Errorf("expected: %q, got: %q", tc.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableDescriptions(t *testing.T) {
|
||||
rootCmd := &Command{
|
||||
Use: "root",
|
||||
Run: emptyRun,
|
||||
}
|
||||
|
||||
childCmd := &Command{
|
||||
Use: "thechild",
|
||||
Short: "The child command",
|
||||
Run: emptyRun,
|
||||
}
|
||||
rootCmd.AddCommand(childCmd)
|
||||
|
||||
specificDescriptionsEnvVar := configEnvVar(rootCmd.Name(), configEnvVarSuffixDescriptions)
|
||||
globalDescriptionsEnvVar := configEnvVar(configEnvVarGlobalPrefix, configEnvVarSuffixDescriptions)
|
||||
|
||||
const (
|
||||
descLineWithDescription = "first\tdescription"
|
||||
descLineWithoutDescription = "first"
|
||||
)
|
||||
childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||
comps := []string{descLineWithDescription}
|
||||
return comps, ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
globalEnvValue string
|
||||
specificEnvValue string
|
||||
expectedLine string
|
||||
}{
|
||||
{
|
||||
"No env variables set",
|
||||
"",
|
||||
"",
|
||||
descLineWithDescription,
|
||||
},
|
||||
{
|
||||
"Global value false",
|
||||
"false",
|
||||
"",
|
||||
descLineWithoutDescription,
|
||||
},
|
||||
{
|
||||
"Specific value false",
|
||||
"",
|
||||
"false",
|
||||
descLineWithoutDescription,
|
||||
},
|
||||
{
|
||||
"Both values false",
|
||||
"false",
|
||||
"false",
|
||||
descLineWithoutDescription,
|
||||
},
|
||||
{
|
||||
"Both values true",
|
||||
"true",
|
||||
"true",
|
||||
descLineWithDescription,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
if err := os.Setenv(specificDescriptionsEnvVar, tc.specificEnvValue); err != nil {
|
||||
t.Errorf("Unexpected error setting %s: %v", specificDescriptionsEnvVar, err)
|
||||
}
|
||||
if err := os.Setenv(globalDescriptionsEnvVar, tc.globalEnvValue); err != nil {
|
||||
t.Errorf("Unexpected error setting %s: %v", globalDescriptionsEnvVar, err)
|
||||
}
|
||||
|
||||
var run = func() {
|
||||
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "thechild", "")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expected := strings.Join([]string{
|
||||
tc.expectedLine,
|
||||
":0",
|
||||
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
||||
if output != expected {
|
||||
t.Errorf("expected: %q, got: %q", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
// For empty cases, test also unset state
|
||||
if tc.specificEnvValue == "" {
|
||||
if err := os.Unsetenv(specificDescriptionsEnvVar); err != nil {
|
||||
t.Errorf("Unexpected error unsetting %s: %v", specificDescriptionsEnvVar, err)
|
||||
}
|
||||
run()
|
||||
}
|
||||
if tc.globalEnvValue == "" {
|
||||
if err := os.Unsetenv(globalDescriptionsEnvVar); err != nil {
|
||||
t.Errorf("Unexpected error unsetting %s: %v", globalDescriptionsEnvVar, err)
|
||||
}
|
||||
run()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,6 +393,9 @@ $ source <(helm completion bash --no-descriptions)
|
|||
$ helm completion [tab][tab]
|
||||
bash fish powershell zsh
|
||||
```
|
||||
|
||||
Setting the `<PROGRAM>_COMPLETION_DESCRIPTIONS` environment variable (falling back to `COBRA_COMPLETION_DESCRIPTIONS` if empty or not set) to a [falsey value](https://pkg.go.dev/strconv#ParseBool) achieves the same. `<PROGRAM>` is the name of your program with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
|
||||
## Bash completions
|
||||
|
||||
### Dependencies
|
||||
|
|
Loading…
Reference in a new issue