mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
Update helpers.go
This commit is contained in:
parent
b6c62d5887
commit
86026a832c
1 changed files with 112 additions and 280 deletions
|
|
@ -14,297 +14,129 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package flags
|
// package flags
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"fmt"
|
// "fmt"
|
||||||
"os"
|
// "os"
|
||||||
"regexp"
|
// "regexp"
|
||||||
"sort"
|
// "sort"
|
||||||
"strings"
|
// "strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
// "github.com/ethereum/go-ethereum/internal/version"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
// "github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
// "github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/mattn/go-isatty"
|
// "github.com/mattn/go-isatty"
|
||||||
"github.com/urfave/cli/v2"
|
// "github.com/urfave/cli/v2"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// usecolor defines whether the CLI help should use colored output or normal dumb
|
// // usecolor defines whether the CLI help should use colored output or normal dumb
|
||||||
// colorless terminal formatting.
|
// // colorless terminal formatting.
|
||||||
var usecolor = (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) && os.Getenv("TERM") != "dumb"
|
// var usecolor = (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) && os.Getenv("TERM") != "dumb"
|
||||||
|
|
||||||
// NewApp creates an app with sane defaults.
|
// // NewApp creates an app with sane defaults.
|
||||||
func NewApp(usage string) *cli.App {
|
// func NewApp(usage string) *cli.App {
|
||||||
git, _ := version.VCS()
|
// git, _ := version.VCS()
|
||||||
app := cli.NewApp()
|
// app := cli.NewApp()
|
||||||
app.EnableBashCompletion = true
|
// app.EnableBashCompletion = true
|
||||||
app.Version = params.VersionWithCommit(git.Commit, git.Date)
|
// app.Version = params.VersionWithCommit(git.Commit, git.Date)
|
||||||
app.Usage = usage
|
// app.Usage = usage
|
||||||
app.Copyright = "Copyright 2013-2024 The go-ethereum Authors"
|
// app.Copyright = "Copyright 2013-2024 The go-ethereum Authors"
|
||||||
app.Before = func(ctx *cli.Context) error {
|
// app.Before = func(ctx *cli.Context) error {
|
||||||
MigrateGlobalFlags(ctx)
|
// MigrateGlobalFlags(ctx)
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
return app
|
// return app
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Merge merges the given flag slices.
|
// // Merge merges the given flag slices.
|
||||||
func Merge(groups ...[]cli.Flag) []cli.Flag {
|
// func Merge(groups ...[]cli.Flag) []cli.Flag {
|
||||||
var ret []cli.Flag
|
// var ret []cli.Flag
|
||||||
for _, group := range groups {
|
// for _, group := range groups {
|
||||||
ret = append(ret, group...)
|
// ret = append(ret, group...)
|
||||||
}
|
// }
|
||||||
return ret
|
// return ret
|
||||||
}
|
// }
|
||||||
|
|
||||||
var migrationApplied = map[*cli.Command]struct{}{}
|
// var migrationApplied = map[*cli.Command]struct{}{}
|
||||||
|
|
||||||
// MigrateGlobalFlags makes all global flag values available in the
|
// // MigrateGlobalFlags makes all global flag values available in the
|
||||||
// context. This should be called as early as possible in app.Before.
|
// // context. This should be called as early as possible in app.Before.
|
||||||
//
|
// func MigrateGlobalFlags(ctx *cli.Context) {
|
||||||
// Example:
|
// var iterate func(cs []*cli.Command, fn func(*cli.Command))
|
||||||
//
|
// iterate = func(cs []*cli.Command, fn func(*cli.Command)) {
|
||||||
// geth account new --keystore /tmp/mykeystore --lightkdf
|
// for _, cmd := range cs {
|
||||||
//
|
// if _, ok := migrationApplied[cmd]; ok {
|
||||||
// is equivalent after calling this method with:
|
// continue
|
||||||
//
|
// }
|
||||||
// geth --keystore /tmp/mykeystore --lightkdf account new
|
// migrationApplied[cmd] = struct{}{}
|
||||||
//
|
// fn(cmd)
|
||||||
// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf)
|
// iterate(cmd.Subcommands, fn)
|
||||||
// will return true even if --lightkdf is set as a global option.
|
// }
|
||||||
//
|
// }
|
||||||
// This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged.
|
|
||||||
func MigrateGlobalFlags(ctx *cli.Context) {
|
|
||||||
var iterate func(cs []*cli.Command, fn func(*cli.Command))
|
|
||||||
iterate = func(cs []*cli.Command, fn func(*cli.Command)) {
|
|
||||||
for _, cmd := range cs {
|
|
||||||
if _, ok := migrationApplied[cmd]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
migrationApplied[cmd] = struct{}{}
|
|
||||||
fn(cmd)
|
|
||||||
iterate(cmd.Subcommands, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This iterates over all commands and wraps their action function.
|
// // This iterates over all commands and wraps their action function.
|
||||||
iterate(ctx.App.Commands, func(cmd *cli.Command) {
|
// iterate(ctx.App.Commands, func(cmd *cli.Command) {
|
||||||
if cmd.Action == nil {
|
// if cmd.Action == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
action := cmd.Action
|
// action := cmd.Action
|
||||||
cmd.Action = func(ctx *cli.Context) error {
|
// cmd.Action = func(ctx *cli.Context) error {
|
||||||
doMigrateFlags(ctx)
|
// doMigrateFlags(ctx)
|
||||||
return action(ctx)
|
// return action(ctx)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
func doMigrateFlags(ctx *cli.Context) {
|
// func doMigrateFlags(ctx *cli.Context) {
|
||||||
// Figure out if there are any aliases of commands. If there are, we want
|
// // Figure out if there are any aliases of commands. If there are, we want
|
||||||
// to ignore them when iterating over the flags.
|
// // to ignore them when iterating over the flags.
|
||||||
aliases := make(map[string]bool)
|
// aliases := make(map[string]bool)
|
||||||
for _, fl := range ctx.Command.Flags {
|
// for _, fl := range ctx.Command.Flags {
|
||||||
for _, alias := range fl.Names()[1:] {
|
// for _, alias := range fl.Names()[1:] {
|
||||||
aliases[alias] = true
|
// aliases[alias] = true
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
for _, name := range ctx.FlagNames() {
|
// for _, name := range ctx.FlagNames() {
|
||||||
for _, parent := range ctx.Lineage()[1:] {
|
// for _, parent := range ctx.Lineage()[1:] {
|
||||||
if parent.IsSet(name) {
|
// if parent.IsSet(name) {
|
||||||
// When iterating across the lineage, we will be served both
|
// if _, isAlias := aliases[name]; isAlias {
|
||||||
// the 'canon' and alias formats of all commands. In most cases,
|
// continue
|
||||||
// it's fine to set it in the ctx multiple times (one for each
|
// }
|
||||||
// name), however, the Slice-flags are not fine.
|
// if result := parent.StringSlice(name); len(result) > 0 {
|
||||||
// The slice-flags accumulate, so if we set it once as
|
// ctx.Set(name, strings.Join(result, ","))
|
||||||
// "foo" and once as alias "F", then both will be present in the slice.
|
// } else {
|
||||||
if _, isAlias := aliases[name]; isAlias {
|
// ctx.Set(name, parent.String(name))
|
||||||
continue
|
// }
|
||||||
}
|
// break
|
||||||
// If it is a string-slice, we need to set it as
|
// }
|
||||||
// "alfa, beta, gamma" instead of "[alfa beta gamma]", in order
|
// }
|
||||||
// for the backing StringSlice to parse it properly.
|
// }
|
||||||
if result := parent.StringSlice(name); len(result) > 0 {
|
// }
|
||||||
ctx.Set(name, strings.Join(result, ","))
|
|
||||||
} else {
|
|
||||||
ctx.Set(name, parent.String(name))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
// func init() {
|
||||||
if usecolor {
|
// if usecolor {
|
||||||
// Annotate all help categories with colors
|
// cli.AppHelpTemplate = regexp.MustCompile("[A-Z ]+:").ReplaceAllString(cli.AppHelpTemplate, "\u001B[33m$0\u001B[0m")
|
||||||
cli.AppHelpTemplate = regexp.MustCompile("[A-Z ]+:").ReplaceAllString(cli.AppHelpTemplate, "\u001B[33m$0\u001B[0m")
|
// cli.AppHelpTemplate = strings.ReplaceAll(cli.AppHelpTemplate, "{{template \"visibleFlagCategoryTemplate\" .}}", "{{range .VisibleFlagCategories}}\n {{if .Name}}\u001B[33m{{.Name}}\u001B[0m\n\n {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}\n{{else}}{{$e}}\n {{end}}{{end}}{{end}}")
|
||||||
|
// }
|
||||||
|
// cli.FlagStringer = FlagString
|
||||||
|
// }
|
||||||
|
|
||||||
// Annotate flag categories with colors (private template, so need to
|
// // FlagString prints a single flag in help.
|
||||||
// copy-paste the entire thing here...)
|
// func FlagString(f cli.Flag) string {
|
||||||
cli.AppHelpTemplate = strings.ReplaceAll(cli.AppHelpTemplate, "{{template \"visibleFlagCategoryTemplate\" .}}", "{{range .VisibleFlagCategories}}\n {{if .Name}}\u001B[33m{{.Name}}\u001B[0m\n\n {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}\n{{else}}{{$e}}\n {{end}}{{end}}{{end}}")
|
// df, ok := f.(cli.DocGenerationFlag)
|
||||||
}
|
// if !ok {
|
||||||
cli.FlagStringer = FlagString
|
// return ""
|
||||||
}
|
// }
|
||||||
|
// needsPlaceholder := df.TakesValue()
|
||||||
|
// placeholder := ""
|
||||||
|
// if needsPlaceholder {
|
||||||
|
// placeholder = "value"
|
||||||
|
// }
|
||||||
|
|
||||||
// FlagString prints a single flag in help.
|
// namesText := cli.FlagNamePrefixer(df.Names(), placeholder)
|
||||||
func FlagString(f cli.Flag) string {
|
|
||||||
df, ok := f.(cli.DocGenerationFlag)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
needsPlaceholder := df.TakesValue()
|
|
||||||
placeholder := ""
|
|
||||||
if needsPlaceholder {
|
|
||||||
placeholder = "value"
|
|
||||||
}
|
|
||||||
|
|
||||||
namesText := cli.FlagNamePrefixer(df.Names(), placeholder)
|
// defaultValueString := ""
|
||||||
|
// if s := df.GetDefaultText(); s != "" {
|
||||||
defaultValueString := ""
|
// default
|
||||||
if s := df.GetDefaultText(); s != "" {
|
|
||||||
defaultValueString = " (default: " + s + ")"
|
|
||||||
}
|
|
||||||
envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), ""))
|
|
||||||
if envHint != "" {
|
|
||||||
envHint = " (" + envHint[1:len(envHint)-1] + ")"
|
|
||||||
}
|
|
||||||
usage := strings.TrimSpace(df.GetUsage())
|
|
||||||
usage = wordWrap(usage, 80)
|
|
||||||
usage = indent(usage, 10)
|
|
||||||
|
|
||||||
if usecolor {
|
|
||||||
return fmt.Sprintf("\n \u001B[32m%-35s%-35s\u001B[0m%s\n%s", namesText, defaultValueString, envHint, usage)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("\n %-35s%-35s%s\n%s", namesText, defaultValueString, envHint, usage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func indent(s string, nspace int) string {
|
|
||||||
ind := strings.Repeat(" ", nspace)
|
|
||||||
return ind + strings.ReplaceAll(s, "\n", "\n"+ind)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wordWrap(s string, width int) string {
|
|
||||||
var (
|
|
||||||
output strings.Builder
|
|
||||||
lineLength = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
sp := strings.IndexByte(s, ' ')
|
|
||||||
var word string
|
|
||||||
if sp == -1 {
|
|
||||||
word = s
|
|
||||||
} else {
|
|
||||||
word = s[:sp]
|
|
||||||
}
|
|
||||||
wlen := len(word)
|
|
||||||
over := lineLength+wlen >= width
|
|
||||||
if over {
|
|
||||||
output.WriteByte('\n')
|
|
||||||
lineLength = 0
|
|
||||||
} else {
|
|
||||||
if lineLength != 0 {
|
|
||||||
output.WriteByte(' ')
|
|
||||||
lineLength++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.WriteString(word)
|
|
||||||
lineLength += wlen
|
|
||||||
|
|
||||||
if sp == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s = s[wlen+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoEnvVars extends all the specific CLI flags with automatically generated
|
|
||||||
// env vars by capitalizing the flag, replacing . with _ and prefixing it with
|
|
||||||
// the specified string.
|
|
||||||
//
|
|
||||||
// Note, the prefix should *not* contain the separator underscore, that will be
|
|
||||||
// added automatically.
|
|
||||||
func AutoEnvVars(flags []cli.Flag, prefix string) {
|
|
||||||
for _, flag := range flags {
|
|
||||||
envvar := strings.ToUpper(prefix + "_" + strings.ReplaceAll(strings.ReplaceAll(flag.Names()[0], ".", "_"), "-", "_"))
|
|
||||||
|
|
||||||
switch flag := flag.(type) {
|
|
||||||
case *cli.StringFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.StringSliceFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.BoolFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.IntFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.Int64Flag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.Uint64Flag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.Float64Flag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.DurationFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *cli.PathFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *BigFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *TextMarshalerFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
|
|
||||||
case *DirectoryFlag:
|
|
||||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckEnvVars iterates over all the environment variables and checks if any of
|
|
||||||
// them look like a CLI flag but is not consumed. This can be used to detect old
|
|
||||||
// or mistyped names.
|
|
||||||
func CheckEnvVars(ctx *cli.Context, flags []cli.Flag, prefix string) {
|
|
||||||
known := make(map[string]string)
|
|
||||||
for _, flag := range flags {
|
|
||||||
docflag, ok := flag.(cli.DocGenerationFlag)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, envvar := range docflag.GetEnvVars() {
|
|
||||||
known[envvar] = flag.Names()[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyvals := os.Environ()
|
|
||||||
sort.Strings(keyvals)
|
|
||||||
|
|
||||||
for _, keyval := range keyvals {
|
|
||||||
key := strings.Split(keyval, "=")[0]
|
|
||||||
if !strings.HasPrefix(key, prefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if flag, ok := known[key]; ok {
|
|
||||||
if ctx.Count(flag) > 0 {
|
|
||||||
log.Info("Config environment variable found", "envvar", key, "shadowedby", "--"+flag)
|
|
||||||
} else {
|
|
||||||
log.Info("Config environment variable found", "envvar", key)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warn("Unknown config environment variable", "envvar", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue