Compare commits

...

12 commits

Author SHA1 Message Date
Johannes Altmanninger 4e07448e7a fish completions: fix double-evaluation of commandline
We capture the commandline tokens using fish's "commandline --tokenize" (-o).
Given a commandline of

	some-cobra-command 'some argument $(123)' "$HOME"

into three arguments while removing the quotes

	some-cobra-command
	some argument $(123)
	$HOME

Later we pass "some argument $(123)" without quotes to the shell's
"eval". This is wrong and causes spurious evaluation of the parenthesis
as command substitution.

Fix this by escaping the arguments.

The upcoming fish 3.8 has a new flag, "commandline -x"
which will expand variables like $HOME properly, see
https://github.com/fish-shell/fish-shell/pull/10212 Use that if
available.

Reproduce the issue by pasting the completion script at
https://github.com/fish-shell/fish-shell/issues/10194#issuecomment-1879563545
to a file "grafana-manager.fish" and running

	function grafana-manager; end
	source grafana-manager.fish

Then type (without pressing Enter)

	grafana-manager public-dashboards delete --organization-id 3 --dashboard-name "k8s (public)" <TAB>

Fixes https://github.com/fish-shell/fish-shell/issues/10194
2024-04-26 18:32:52 +02:00
dependabot[bot] 5a1acea321
build(deps): bump github.com/cpuguy83/go-md2man/v2 from 2.0.3 to 2.0.4 (#2127) 2024-04-13 02:21:03 +00:00
Xinwei Xiong 0fc86c2ffd
docs: update user guide (#2128) 2024-04-08 06:47:35 -04:00
Ville Skyttä 6b5f577ebc
More linting (#2099)
* Address gocritic findings, enable it

* Enable gosimple, no new findings to address
2024-04-01 08:42:08 -04:00
Pedro Mota bd914e58d6
fix: remove deprecated io/ioutils package (#2120)
ioutils.ReadAll is deprecated since Go 1.16. This commit replaces it with
io.ReadAll. See https://pkg.go.dev/io/ioutil\#ReadAll for reference

Issue #2119
2024-03-12 06:42:46 -04:00
racerole 1f80fa2e23
chore: remove repetitive words (#2122)
Signed-off-by: racerole <jiangyifeng@outlook.com>
2024-03-12 06:40:19 -04:00
damas c69ae4c36b
ci: test golang 1.22 (#2113) 2024-03-12 06:40:01 -04:00
dependabot[bot] a30cee5e5a
build(deps): bump actions/cache from 3 to 4 (#2102)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 19:36:37 -06:00
dependabot[bot] f34069ccf5
build(deps): bump golangci/golangci-lint-action from 3.7.0 to 4.0.0 (#2108)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.7.0 to 4.0.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.7.0...v4.0.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 19:35:05 -06:00
montag451 bd2655e76c
Add Incus to the list of projects using Cobra (#2118) 2024-03-07 19:36:58 -05:00
Radek Smid bcfcff729e
Add Taikun CloudWorks to list of projects (#2098) 2024-01-15 08:38:50 -05:00
Dmytro Milinevskyi 4fb0a66a34
flags: clarify documentation that LocalFlags related function do not modify the state (#2064) 2024-01-06 16:49:13 -05:00
15 changed files with 49 additions and 35 deletions

View file

@ -43,11 +43,11 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '^1.21' go-version: '^1.22'
check-latest: true check-latest: true
cache: true cache: true
- uses: golangci/golangci-lint-action@v3.7.0 - uses: golangci/golangci-lint-action@v4.0.0
with: with:
version: latest version: latest
args: --verbose args: --verbose
@ -66,6 +66,7 @@ jobs:
- 19 - 19
- 20 - 20
- 21 - 21
- 22
name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x'
runs-on: ${{ matrix.platform }}-latest runs-on: ${{ matrix.platform }}-latest
steps: steps:
@ -108,7 +109,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v3 - uses: actions/cache@v4
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}

View file

@ -29,7 +29,7 @@ linters:
- gas - gas
#- gochecknoinits #- gochecknoinits
- goconst - goconst
#- gocritic - gocritic
#- gocyclo #- gocyclo
#- gofmt #- gofmt
- goimports - goimports
@ -37,7 +37,7 @@ linters:
#- gomnd #- gomnd
#- goprintffuncname #- goprintffuncname
#- gosec #- gosec
#- gosimple - gosimple
- govet - govet
- ineffassign - ineffassign
- interfacer - interfacer

View file

@ -597,9 +597,7 @@ func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
if nonCompletableFlag(flag) { if nonCompletableFlag(flag) {
return return
} }
for key := range flag.Annotations { if _, ok := flag.Annotations[BashCompOneRequiredFlag]; ok {
switch key {
case BashCompOneRequiredFlag:
format := " must_have_one_flag+=(\"--%s" format := " must_have_one_flag+=(\"--%s"
if flag.Value.Type() != "bool" { if flag.Value.Type() != "bool" {
format += "=" format += "="
@ -611,7 +609,6 @@ func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand)) WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
} }
} }
}
}) })
} }

View file

@ -154,8 +154,10 @@ type Command struct {
// pflags contains persistent flags. // pflags contains persistent flags.
pflags *flag.FlagSet pflags *flag.FlagSet
// lflags contains local flags. // lflags contains local flags.
// This field does not represent internal state, it's used as a cache to optimise LocalFlags function call
lflags *flag.FlagSet lflags *flag.FlagSet
// iflags contains inherited flags. // iflags contains inherited flags.
// This field does not represent internal state, it's used as a cache to optimise InheritedFlags function call
iflags *flag.FlagSet iflags *flag.FlagSet
// parentsPflags is all persistent flags of cmd's parents. // parentsPflags is all persistent flags of cmd's parents.
parentsPflags *flag.FlagSet parentsPflags *flag.FlagSet
@ -1659,6 +1661,7 @@ func (c *Command) Flags() *flag.FlagSet {
} }
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands. // LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands.
// This function does not modify the flags of the current command, it's purpose is to return the current state.
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
persistentFlags := c.PersistentFlags() persistentFlags := c.PersistentFlags()
@ -1672,6 +1675,7 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
} }
// LocalFlags returns the local FlagSet specifically set in the current command. // LocalFlags returns the local FlagSet specifically set in the current command.
// This function does not modify the flags of the current command, it's purpose is to return the current state.
func (c *Command) LocalFlags() *flag.FlagSet { func (c *Command) LocalFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
@ -1699,6 +1703,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
} }
// InheritedFlags returns all flags which were inherited from parent commands. // InheritedFlags returns all flags which were inherited from parent commands.
// This function does not modify the flags of the current command, it's purpose is to return the current state.
func (c *Command) InheritedFlags() *flag.FlagSet { func (c *Command) InheritedFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
@ -1724,6 +1729,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
} }
// NonInheritedFlags returns all flags which were not inherited from parent commands. // NonInheritedFlags returns all flags which were not inherited from parent commands.
// This function does not modify the flags of the current command, it's purpose is to return the current state.
func (c *Command) NonInheritedFlags() *flag.FlagSet { func (c *Command) NonInheritedFlags() *flag.FlagSet {
return c.LocalFlags() return c.LocalFlags()
} }

View file

@ -18,7 +18,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"io/ioutil" "io"
"os" "os"
"reflect" "reflect"
"strings" "strings"
@ -2092,12 +2092,12 @@ func TestCommandPrintRedirection(t *testing.T) {
t.Error(err) t.Error(err)
} }
gotErrBytes, err := ioutil.ReadAll(errBuff) gotErrBytes, err := io.ReadAll(errBuff)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
gotOutBytes, err := ioutil.ReadAll(outBuff) gotOutBytes, err := io.ReadAll(outBuff)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -2777,7 +2777,7 @@ func TestFind(t *testing.T) {
func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) { func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
testCases := [][]string{ testCases := [][]string{
//{"--unknown", "--namespace", "foo", "child", "--bar"}, // FIXME: This test case fails, returning the error `unknown command "foo" for "root"` instead of the expected error `unknown flag: --unknown` // {"--unknown", "--namespace", "foo", "child", "--bar"}, // FIXME: This test case fails, returning the error `unknown command "foo" for "root"` instead of the expected error `unknown flag: --unknown`
{"--namespace", "foo", "--unknown", "child", "--bar"}, {"--namespace", "foo", "--unknown", "child", "--bar"},
{"--namespace", "foo", "child", "--unknown", "--bar"}, {"--namespace", "foo", "child", "--unknown", "--bar"},
{"--namespace", "foo", "child", "--bar", "--unknown"}, {"--namespace", "foo", "child", "--bar", "--unknown"},

View file

@ -133,7 +133,7 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
} }
header.Date = &now header.Date = &now
} }
header.date = (*header.Date).Format("Jan 2006") header.date = header.Date.Format("Jan 2006")
if header.Source == "" && !disableAutoGen { if header.Source == "" && !disableAutoGen {
header.Source = "Auto generated by spf13/cobra" header.Source = "Auto generated by spf13/cobra"
} }

View file

@ -128,7 +128,7 @@ func GenMarkdownTree(cmd *cobra.Command, dir string) error {
return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity) return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
} }
// GenMarkdownTreeCustom is the the same as GenMarkdownTree, but // GenMarkdownTreeCustom is the same as GenMarkdownTree, but
// with custom filePrepender and linkHandler. // with custom filePrepender and linkHandler.
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {

View file

@ -140,7 +140,7 @@ func GenReSTTree(cmd *cobra.Command, dir string) error {
return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler) return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler)
} }
// GenReSTTreeCustom is the the same as GenReSTTree, but // GenReSTTreeCustom is the same as GenReSTTree, but
// with custom filePrepender and linkHandler. // with custom filePrepender and linkHandler.
func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error { func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {

View file

@ -40,7 +40,7 @@ func hasSeeAlso(cmd *cobra.Command) bool {
// that do not contain \n. // that do not contain \n.
func forceMultiLine(s string) string { func forceMultiLine(s string) string {
if len(s) > 60 && !strings.Contains(s, "\n") { if len(s) > 60 && !strings.Contains(s, "\n") {
s = s + "\n" s += "\n"
} }
return s return s
} }

View file

@ -45,7 +45,9 @@ function __%[1]s_perform_completion
__%[1]s_debug "Starting __%[1]s_perform_completion" __%[1]s_debug "Starting __%[1]s_perform_completion"
# Extract all args except the last one # Extract all args except the last one
set -l args (commandline -opc) set -l args (
commandline -xpc 2>/dev/null ||
commandline -opc | string escape)
# Extract the last arg and escape it in case it is a space # Extract the last arg and escape it in case it is a space
set -l lastArg (string escape -- (commandline -ct)) set -l lastArg (string escape -- (commandline -ct))

2
go.mod
View file

@ -3,7 +3,7 @@ module github.com/spf13/cobra
go 1.15 go 1.15
require ( require (
github.com/cpuguy83/go-md2man/v2 v2.0.3 github.com/cpuguy83/go-md2man/v2 v2.0.4
github.com/inconshreveable/mousetrap v1.1.0 github.com/inconshreveable/mousetrap v1.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1

4
go.sum
View file

@ -1,5 +1,5 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=

View file

@ -28,8 +28,8 @@ import (
func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) { func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character // Variables should not contain a '-' or ':' character
nameForVar := name nameForVar := name
nameForVar = strings.Replace(nameForVar, "-", "_", -1) nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
nameForVar = strings.Replace(nameForVar, ":", "_", -1) nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
compCmd := ShellCompRequestCmd compCmd := ShellCompRequestCmd
if !includeDesc { if !includeDesc {

View file

@ -24,6 +24,7 @@
- [GoReleaser](https://goreleaser.com) - [GoReleaser](https://goreleaser.com)
- [Helm](https://helm.sh) - [Helm](https://helm.sh)
- [Hugo](https://gohugo.io) - [Hugo](https://gohugo.io)
- [Incus](https://linuxcontainers.org/incus/)
- [Infracost](https://github.com/infracost/infracost) - [Infracost](https://github.com/infracost/infracost)
- [Istio](https://istio.io) - [Istio](https://istio.io)
- [Kool](https://github.com/kool-dev/kool) - [Kool](https://github.com/kool-dev/kool)
@ -57,6 +58,7 @@
- [Scaleway CLI](https://github.com/scaleway/scaleway-cli) - [Scaleway CLI](https://github.com/scaleway/scaleway-cli)
- [Sia](https://github.com/SiaFoundation/siad) - [Sia](https://github.com/SiaFoundation/siad)
- [Skaffold](https://skaffold.dev/) - [Skaffold](https://skaffold.dev/)
- [Taikun](https://taikun.cloud/)
- [Tendermint](https://github.com/tendermint/tendermint) - [Tendermint](https://github.com/tendermint/tendermint)
- [Twitch CLI](https://github.com/twitchdev/twitch-cli) - [Twitch CLI](https://github.com/twitchdev/twitch-cli)
- [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli) - [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli)

View file

@ -3,7 +3,7 @@
While you are welcome to provide your own organization, typically a Cobra-based While you are welcome to provide your own organization, typically a Cobra-based
application will follow the following organizational structure: application will follow the following organizational structure:
``` ```test
▾ appName/ ▾ appName/
▾ cmd/ ▾ cmd/
add.go add.go
@ -301,6 +301,7 @@ command := cobra.Command{
### Bind Flags with Config ### Bind Flags with Config
You can also bind your flags with [viper](https://github.com/spf13/viper): You can also bind your flags with [viper](https://github.com/spf13/viper):
```go ```go
var author string var author string
@ -320,12 +321,14 @@ More in [viper documentation](https://github.com/spf13/viper#working-with-flags)
Flags are optional by default. If instead you wish your command to report an error Flags are optional by default. If instead you wish your command to report an error
when a flag has not been set, mark it as required: when a flag has not been set, mark it as required:
```go ```go
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)") rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region") rootCmd.MarkFlagRequired("region")
``` ```
Or, for persistent flags: Or, for persistent flags:
```go ```go
rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)") rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkPersistentFlagRequired("region") rootCmd.MarkPersistentFlagRequired("region")
@ -335,6 +338,7 @@ rootCmd.MarkPersistentFlagRequired("region")
If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then
Cobra can enforce that requirement: Cobra can enforce that requirement:
```go ```go
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)") rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)") rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
@ -343,6 +347,7 @@ rootCmd.MarkFlagsRequiredTogether("username", "password")
You can also prevent different flags from being provided together if they represent mutually You can also prevent different flags from being provided together if they represent mutually
exclusive options such as specifying an output format as either `--json` or `--yaml` but never both: exclusive options such as specifying an output format as either `--json` or `--yaml` but never both:
```go ```go
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
@ -351,6 +356,7 @@ rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
If you want to require at least one flag from a group to be present, you can use `MarkFlagsOneRequired`. If you want to require at least one flag from a group to be present, you can use `MarkFlagsOneRequired`.
This can be combined with `MarkFlagsMutuallyExclusive` to enforce exactly one flag from a given group: This can be combined with `MarkFlagsMutuallyExclusive` to enforce exactly one flag from a given group:
```go ```go
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
@ -428,7 +434,7 @@ by not providing a 'Run' for the 'rootCmd'.
We have only defined one flag for a single command. We have only defined one flag for a single command.
More documentation about flags is available at https://github.com/spf13/pflag More documentation about flags is available at https://github.com/spf13/pflag.
```go ```go
package main package main
@ -722,7 +728,7 @@ command.SuggestionsMinimumDistance = 1
You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which
you don't want aliases. Example: you don't want aliases. Example:
``` ```bash
$ kubectl remove $ kubectl remove
Error: unknown command "remove" for "kubectl" Error: unknown command "remove" for "kubectl"
@ -787,7 +793,7 @@ func main() {
Example run as a kubectl plugin: Example run as a kubectl plugin:
``` ```bash
$ kubectl myplugin $ kubectl myplugin
Usage: Usage:
kubectl myplugin [command] kubectl myplugin [command]