Skip to content

Commit 1291513

Browse files
committed
Copy cobra helper of etcdctl from main pkg to etcdctl pkg and add a deprecation note for UsageFunc.
Signed-off-by: ah8ad3 <[email protected]>
1 parent a423349 commit 1291513

File tree

3 files changed

+183
-1
lines changed

3 files changed

+183
-1
lines changed

etcdctl/ctlv3/ctl.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"go.etcd.io/etcd/api/v3/version"
2525
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command"
26+
"go.etcd.io/etcd/etcdctl/v3/util"
2627
"go.etcd.io/etcd/pkg/v3/cobrautl"
2728
)
2829

@@ -102,7 +103,7 @@ func init() {
102103
}
103104

104105
func usageFunc(c *cobra.Command) error {
105-
return cobrautl.UsageFunc(c, version.Version, version.APIVersion)
106+
return util.UsageFunc(c, version.Version, version.APIVersion)
106107
}
107108

108109
func Start() error {

etcdctl/util/help.go

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright 2025 The etcd Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// copied from https://github.com/rkt/rkt/blob/master/rkt/help.go
16+
17+
package util
18+
19+
import (
20+
"fmt"
21+
"io"
22+
"os"
23+
"strings"
24+
"text/tabwriter"
25+
"text/template"
26+
27+
"github.com/spf13/cobra"
28+
"github.com/spf13/pflag"
29+
)
30+
31+
var (
32+
commandUsageTemplate *template.Template
33+
templFuncs = template.FuncMap{
34+
"descToLines": func(s string) []string {
35+
// trim leading/trailing whitespace and split into slice of lines
36+
return strings.Split(strings.Trim(s, "\n\t "), "\n")
37+
},
38+
"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
39+
parts := []string{cmd.Name()}
40+
for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
41+
cmd = cmd.Parent()
42+
parts = append([]string{cmd.Name()}, parts...)
43+
}
44+
return strings.Join(parts, " ")
45+
},
46+
"indent": func(s string) string {
47+
pad := strings.Repeat(" ", 2)
48+
return pad + strings.Replace(s, "\n", "\n"+pad, -1)
49+
},
50+
}
51+
)
52+
53+
func init() {
54+
commandUsage := `
55+
{{ $cmd := .Cmd }}\
56+
{{ $cmdname := cmdName .Cmd .Cmd.Root }}\
57+
NAME:
58+
{{if not .Cmd.HasParent}}\
59+
{{printf "%s - %s" .Cmd.Name .Cmd.Short | indent}}
60+
{{else}}\
61+
{{printf "%s - %s" $cmdname .Cmd.Short | indent}}
62+
{{end}}\
63+
64+
USAGE:
65+
{{printf "%s" .Cmd.UseLine | indent}}
66+
{{ if not .Cmd.HasParent }}\
67+
68+
VERSION:
69+
{{printf "%s" .Version | indent}}
70+
{{end}}\
71+
{{if .Cmd.HasSubCommands}}\
72+
73+
API VERSION:
74+
{{.APIVersion | indent}}
75+
{{end}}\
76+
{{if .Cmd.HasExample}}\
77+
78+
Examples:
79+
{{.Cmd.Example}}
80+
{{end}}\
81+
{{if .Cmd.HasSubCommands}}\
82+
83+
COMMANDS:
84+
{{range .SubCommands}}\
85+
{{ $cmdname := cmdName . $cmd }}\
86+
{{ if .Runnable }}\
87+
{{printf "%s\t%s" $cmdname .Short | indent}}
88+
{{end}}\
89+
{{end}}\
90+
{{end}}\
91+
{{ if .Cmd.Long }}\
92+
93+
DESCRIPTION:
94+
{{range $line := descToLines .Cmd.Long}}{{printf "%s" $line | indent}}
95+
{{end}}\
96+
{{end}}\
97+
{{if .Cmd.HasLocalFlags}}\
98+
99+
OPTIONS:
100+
{{.LocalFlags}}\
101+
{{end}}\
102+
{{if .Cmd.HasInheritedFlags}}\
103+
104+
GLOBAL OPTIONS:
105+
{{.GlobalFlags}}\
106+
{{end}}
107+
`[1:]
108+
109+
commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.ReplaceAll(commandUsage, "\\\n", "")))
110+
}
111+
112+
func etcdFlagUsages(flagSet *pflag.FlagSet) string {
113+
x := new(strings.Builder)
114+
115+
flagSet.VisitAll(func(flag *pflag.Flag) {
116+
if len(flag.Deprecated) > 0 {
117+
return
118+
}
119+
var format string
120+
if len(flag.Shorthand) > 0 {
121+
format = " -%s, --%s"
122+
} else {
123+
format = " %s --%s"
124+
}
125+
if len(flag.NoOptDefVal) > 0 {
126+
format = format + "["
127+
}
128+
if flag.Value.Type() == "string" {
129+
// put quotes on the value
130+
format = format + "=%q"
131+
} else {
132+
format = format + "=%s"
133+
}
134+
if len(flag.NoOptDefVal) > 0 {
135+
format = format + "]"
136+
}
137+
format = format + "\t%s\n"
138+
shorthand := flag.Shorthand
139+
fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
140+
})
141+
142+
return x.String()
143+
}
144+
145+
func getSubCommands(cmd *cobra.Command) []*cobra.Command {
146+
var subCommands []*cobra.Command
147+
for _, subCmd := range cmd.Commands() {
148+
subCommands = append(subCommands, subCmd)
149+
subCommands = append(subCommands, getSubCommands(subCmd)...)
150+
}
151+
return subCommands
152+
}
153+
154+
func UsageFunc(cmd *cobra.Command, version, APIVersion string) error {
155+
subCommands := getSubCommands(cmd)
156+
tabOut := getTabOutWithWriter(os.Stdout)
157+
commandUsageTemplate.Execute(tabOut, struct {
158+
Cmd *cobra.Command
159+
LocalFlags string
160+
GlobalFlags string
161+
SubCommands []*cobra.Command
162+
Version string
163+
APIVersion string
164+
}{
165+
cmd,
166+
etcdFlagUsages(cmd.LocalFlags()),
167+
etcdFlagUsages(cmd.InheritedFlags()),
168+
subCommands,
169+
version,
170+
APIVersion,
171+
})
172+
tabOut.Flush()
173+
return nil
174+
}
175+
176+
func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer {
177+
aTabOut := new(tabwriter.Writer)
178+
aTabOut.Init(writer, 0, 8, 1, '\t', 0)
179+
return aTabOut
180+
}

pkg/cobrautl/help.go

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func getSubCommands(cmd *cobra.Command) []*cobra.Command {
151151
return subCommands
152152
}
153153

154+
// Deprecated: Please use go.etcd.io/etcd/etcdctl/v3/util instead.
154155
func UsageFunc(cmd *cobra.Command, version, APIVersion string) error {
155156
subCommands := getSubCommands(cmd)
156157
tabOut := getTabOutWithWriter(os.Stdout)

0 commit comments

Comments
 (0)