2020-08-06 20:58:47 +03:00
|
|
|
// Package cli implements sq's CLI. The spf13/cobra library
|
2023-05-19 17:24:18 +03:00
|
|
|
// provides the core command processing.
|
2023-04-19 08:28:09 +03:00
|
|
|
//
|
|
|
|
// Although cobra provides excellent functionality, it has some issues.
|
2020-08-06 20:58:47 +03:00
|
|
|
// Most prominently, its documentation suggests reliance
|
|
|
|
// upon package-level constructs for initializing the
|
2020-12-30 22:18:22 +03:00
|
|
|
// command tree (bad for testing).
|
2020-08-06 20:58:47 +03:00
|
|
|
//
|
|
|
|
// Thus, this cmd package deviates from cobra's suggested
|
|
|
|
// usage pattern by eliminating all pkg-level constructs
|
2023-04-19 08:28:09 +03:00
|
|
|
// (which makes testing easier).
|
2020-12-30 22:18:22 +03:00
|
|
|
//
|
2023-06-13 19:06:18 +03:00
|
|
|
// All interaction with cobra should happen inside this package, or
|
|
|
|
// via the utility cli/cobraz package.
|
2023-05-19 17:24:18 +03:00
|
|
|
// That is to say, the spf13/cobra package should not be imported
|
2023-06-13 19:06:18 +03:00
|
|
|
// anywhere outside this package and cli/cobraz.
|
2023-05-19 17:24:18 +03:00
|
|
|
//
|
2020-08-06 20:58:47 +03:00
|
|
|
// The entry point to this pkg is the Execute function.
|
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2021-02-22 10:37:00 +03:00
|
|
|
"sync"
|
2024-01-29 19:02:42 +03:00
|
|
|
"time"
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2024-01-29 19:02:42 +03:00
|
|
|
"github.com/c2h5oh/datasize"
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/spf13/cobra"
|
2023-05-03 15:36:10 +03:00
|
|
|
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/neilotoole/sq/cli/buildinfo"
|
2024-01-31 09:04:10 +03:00
|
|
|
"github.com/neilotoole/sq/cli/cobraz"
|
2023-04-19 08:28:09 +03:00
|
|
|
"github.com/neilotoole/sq/cli/flag"
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/neilotoole/sq/cli/run"
|
2024-01-29 18:16:21 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/ioz"
|
2023-04-02 22:49:45 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/lg"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/lg/lga"
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/options"
|
2020-08-06 20:58:47 +03:00
|
|
|
)
|
|
|
|
|
2022-12-18 09:07:38 +03:00
|
|
|
func init() { //nolint:gochecknoinits
|
2021-02-22 10:37:00 +03:00
|
|
|
cobra.EnableCommandSorting = false
|
|
|
|
}
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
const (
|
|
|
|
msgInvalidArgs = "invalid args"
|
|
|
|
msgNoActiveSrc = "no active data source"
|
|
|
|
msgEmptyQueryString = "query string is empty"
|
|
|
|
msgSrcNoData = "source has no data"
|
|
|
|
msgSrcEmptyTableName = "source has empty table name"
|
|
|
|
)
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// errNoMsg is a sentinel error indicating that a command
|
2024-01-15 04:45:34 +03:00
|
|
|
// has failed (and thus the program should exit with a non-zero
|
|
|
|
// code), but no error message should be printed.
|
2020-08-06 20:58:47 +03:00
|
|
|
// This is useful in the case where any error information may
|
|
|
|
// already have been printed as part of the command output.
|
|
|
|
var errNoMsg = errors.New("")
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
// Execute builds a Run using ctx and default
|
2020-08-06 20:58:47 +03:00
|
|
|
// settings, and invokes ExecuteWith.
|
|
|
|
func Execute(ctx context.Context, stdin *os.File, stdout, stderr io.Writer, args []string) error {
|
2023-05-19 17:24:18 +03:00
|
|
|
ru, log, err := newRun(ctx, stdin, stdout, stderr, args)
|
2020-08-06 20:58:47 +03:00
|
|
|
if err != nil {
|
2024-01-15 04:45:34 +03:00
|
|
|
// This may be unnecessary, but we are extra-paranoid about
|
|
|
|
// closing ru before exiting the program.
|
|
|
|
if closeErr := ru.Close(); closeErr != nil && log != nil {
|
|
|
|
log.Error("Failed to close run", lga.Err, closeErr)
|
|
|
|
}
|
|
|
|
if ru.LogCloser != nil {
|
|
|
|
_ = ru.LogCloser()
|
|
|
|
}
|
2024-01-25 07:01:24 +03:00
|
|
|
PrintError(ctx, ru, err)
|
2020-08-06 20:58:47 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-04-30 17:18:56 +03:00
|
|
|
ctx = lg.NewContext(ctx, log)
|
2023-05-19 17:24:18 +03:00
|
|
|
return ExecuteWith(ctx, ru, args)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ExecuteWith invokes the cobra CLI framework, ultimately
|
2024-01-15 04:45:34 +03:00
|
|
|
// resulting in a command being executed. This function always closes ru.
|
2023-05-19 17:24:18 +03:00
|
|
|
func ExecuteWith(ctx context.Context, ru *run.Run, args []string) error {
|
2024-01-15 04:45:34 +03:00
|
|
|
defer func() {
|
|
|
|
if ru != nil && ru.LogCloser != nil {
|
|
|
|
_ = ru.LogCloser()
|
|
|
|
}
|
|
|
|
}()
|
2024-01-29 18:16:21 +03:00
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
ctx = options.NewContext(ctx, options.Merge(options.FromContext(ctx), ru.Config.Options))
|
2023-05-03 15:36:10 +03:00
|
|
|
log := lg.FromContext(ctx)
|
2024-01-15 04:45:34 +03:00
|
|
|
log.Info("EXECUTE", "args", strings.Join(args, " "))
|
|
|
|
log.Info("Build info", "build", buildinfo.Get())
|
|
|
|
log.Info("Config", "config_version", ru.Config.Version,
|
2023-05-19 17:24:18 +03:00
|
|
|
lga.Path, ru.ConfigStore.Location(),
|
2023-04-02 22:49:45 +03:00
|
|
|
)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
ctx = run.NewContext(ctx, ru)
|
2021-02-22 10:37:00 +03:00
|
|
|
|
2024-01-29 18:16:21 +03:00
|
|
|
if freq := OptDebugTrackMemory.Get(options.FromContext(ctx)); freq > 0 {
|
|
|
|
// Debug setting to log peak memory usage on exit.
|
2024-01-29 19:02:42 +03:00
|
|
|
peakSys, peakAllocs, gcPause := ioz.StartMemStatsTracker(ctx, freq)
|
2024-01-29 18:16:21 +03:00
|
|
|
defer func() {
|
2024-01-29 19:02:42 +03:00
|
|
|
log.Info("Peak memory stats",
|
|
|
|
"sys", datasize.ByteSize(peakSys.Load()).HR(),
|
|
|
|
"heap", datasize.ByteSize(peakAllocs.Load()).HR(),
|
|
|
|
"gc_pause", time.Duration(gcPause.Load()).String(),
|
|
|
|
)
|
2024-01-29 18:16:21 +03:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
rootCmd := newCommandTree(ru)
|
2021-02-22 10:37:00 +03:00
|
|
|
var err error
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2020-12-30 22:18:22 +03:00
|
|
|
// The following is a workaround for the fact that cobra doesn't
|
2023-04-19 08:28:09 +03:00
|
|
|
// currently (as of 2017, so yeah, "currently") support executing
|
|
|
|
// the root command with arbitrary args. That is to say, if you execute:
|
2020-08-06 20:58:47 +03:00
|
|
|
//
|
2021-02-22 10:37:00 +03:00
|
|
|
// $ sq @sakila_sl3.actor
|
2020-08-06 20:58:47 +03:00
|
|
|
//
|
2021-02-22 10:37:00 +03:00
|
|
|
// then cobra will look for a command named "@sakila_sl3.actor",
|
|
|
|
// and when it doesn't find such a command, it returns
|
|
|
|
// an "unknown command" error.
|
|
|
|
|
2024-01-31 09:04:10 +03:00
|
|
|
switch {
|
|
|
|
case len(args) > 0 && (args[0] == cobra.ShellCompRequestCmd || args[0] == cobra.ShellCompNoDescRequestCmd):
|
|
|
|
// Look for `sq __complete X` or `sq __completeNoDesc X`
|
2024-01-25 07:01:24 +03:00
|
|
|
if !OptShellCompletionLog.Get(options.FromContext(ctx)) {
|
|
|
|
log.Debug("Discarding shell completion logging",
|
|
|
|
lga.Opt, OptShellCompletionLog.Key(), lga.Val, false)
|
|
|
|
ctx = lg.NewContext(ctx, lg.Discard())
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
if hasMatchingChildCommand(rootCmd, args[1]) {
|
|
|
|
// If there is a matching child command, we let rootCmd
|
|
|
|
// handle it, as per normal.
|
|
|
|
rootCmd.SetArgs(args)
|
|
|
|
} else {
|
2024-01-31 09:04:10 +03:00
|
|
|
// There's no command matching the first argument
|
|
|
|
// to "__complete" / "__completeNoDesc".
|
2021-02-22 10:37:00 +03:00
|
|
|
// Therefore, we assume that we want to perform completion
|
|
|
|
// for the "slq" command (which is the pseudo-root command).
|
2024-01-31 09:04:10 +03:00
|
|
|
effectiveArgs := append([]string{args[0], "slq"}, args[1:]...)
|
2021-02-22 10:37:00 +03:00
|
|
|
rootCmd.SetArgs(effectiveArgs)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
2024-01-31 09:04:10 +03:00
|
|
|
case len(args) > 0 && args[0] == cobraz.ShellCompGenScriptsCmd:
|
|
|
|
// Look for `sq completion [bash|zsh|X]`, which is cobra's built-in cmd
|
|
|
|
// to generate shell completion scripts. This is NOT the same cmd as the
|
|
|
|
// cobra.ShellCompRequestCmd ("__complete") which actually returns
|
|
|
|
// completion values. The cobraz.ShellCompGenScriptsCmd cmd won't be in the
|
|
|
|
// command tree (the call to rootCmd.Find below won't return it), so we need
|
|
|
|
// to look specifically for its name in the args, and when found, we
|
|
|
|
// let rootCmd take over, which will eventually find that built-in cmd.
|
|
|
|
rootCmd.SetArgs(args)
|
|
|
|
default:
|
2021-02-22 10:37:00 +03:00
|
|
|
var cmd *cobra.Command
|
|
|
|
cmd, _, err = rootCmd.Find(args)
|
|
|
|
if err != nil {
|
|
|
|
// This err will be the "unknown command" error.
|
|
|
|
// cobra still returns cmd though. It should be
|
|
|
|
// the root cmd.
|
|
|
|
if cmd == nil || cmd.Name() != rootCmd.Name() {
|
2024-01-15 04:45:34 +03:00
|
|
|
lg.WarnIfCloseError(log, "Problem closing run", ru)
|
2021-02-22 10:37:00 +03:00
|
|
|
// Not sure if this can happen anymore? Can prob delete?
|
|
|
|
panic(fmt.Sprintf("bad cobra cmd state: %v", cmd))
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have args [sq, arg1, arg2] then we redirect
|
|
|
|
// to the "slq" command by modifying args to
|
|
|
|
// look like: [query, arg1, arg2] -- noting that SetArgs
|
|
|
|
// doesn't want the first args element.
|
|
|
|
effectiveArgs := append([]string{"slq"}, args...)
|
2023-04-07 11:00:49 +03:00
|
|
|
if effectiveArgs, err = preprocessFlagArgVars(effectiveArgs); err != nil {
|
2024-01-15 04:45:34 +03:00
|
|
|
lg.WarnIfCloseError(log, "Problem closing run", ru)
|
2023-04-07 11:00:49 +03:00
|
|
|
return err
|
|
|
|
}
|
2021-02-22 10:37:00 +03:00
|
|
|
rootCmd.SetArgs(effectiveArgs)
|
2020-08-06 20:58:47 +03:00
|
|
|
} else {
|
2021-02-22 10:37:00 +03:00
|
|
|
if cmd.Name() == rootCmd.Name() {
|
|
|
|
// Not sure why we have two paths to this, but it appears
|
|
|
|
// that we've found the root cmd again, so again
|
|
|
|
// we redirect to "slq" cmd.
|
2023-04-07 11:00:49 +03:00
|
|
|
effectiveArgs := append([]string{"slq"}, args...)
|
|
|
|
if effectiveArgs, err = preprocessFlagArgVars(effectiveArgs); err != nil {
|
2024-01-15 04:45:34 +03:00
|
|
|
lg.WarnIfCloseError(log, "Problem closing run", ru)
|
2023-04-07 11:00:49 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
rootCmd.SetArgs(effectiveArgs)
|
2021-02-22 10:37:00 +03:00
|
|
|
} else {
|
|
|
|
// It's just a normal command like "sq ls" or such.
|
|
|
|
|
|
|
|
// Explicitly set the args on rootCmd as this makes
|
|
|
|
// cobra happy when this func is executed via tests.
|
|
|
|
// Haven't explored the reason why.
|
|
|
|
rootCmd.SetArgs(args)
|
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
rootCmd.SetContext(ctx)
|
2021-02-22 10:37:00 +03:00
|
|
|
// Execute rootCmd; cobra will find the appropriate
|
2020-08-06 20:58:47 +03:00
|
|
|
// sub-command, and ultimately execute that command.
|
2021-02-22 10:37:00 +03:00
|
|
|
err = rootCmd.ExecuteContext(ctx)
|
2024-01-15 04:45:34 +03:00
|
|
|
lg.WarnIfCloseError(log, "Problem closing run", ru)
|
2020-08-06 20:58:47 +03:00
|
|
|
if err != nil {
|
2024-01-25 07:01:24 +03:00
|
|
|
PrintError(ctx, ru, err)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
// cobraMu exists because cobra relies upon package-level
|
|
|
|
// constructs. This does not sit well with parallel tests.
|
|
|
|
var cobraMu sync.Mutex
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// newCommandTree builds sq's command tree, returning
|
|
|
|
// the root cobra command.
|
2023-05-19 17:24:18 +03:00
|
|
|
func newCommandTree(ru *run.Run) (rootCmd *cobra.Command) {
|
2021-02-22 10:37:00 +03:00
|
|
|
cobraMu.Lock()
|
|
|
|
defer cobraMu.Unlock()
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
rootCmd = newRootCmd()
|
2022-12-30 05:02:10 +03:00
|
|
|
rootCmd.DisableAutoGenTag = true
|
2023-05-19 17:24:18 +03:00
|
|
|
rootCmd.SetOut(ru.Out)
|
|
|
|
rootCmd.SetErr(ru.ErrOut)
|
2022-12-17 07:59:42 +03:00
|
|
|
rootCmd.Flags().SortFlags = false
|
2023-05-22 18:08:14 +03:00
|
|
|
rootCmd.PersistentFlags().SortFlags = false
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
helpCmd := addCmd(ru, rootCmd, newHelpCmd())
|
2020-08-06 20:58:47 +03:00
|
|
|
rootCmd.SetHelpCommand(helpCmd)
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
// From the end user's perspective, slqCmd is *effectively* the
|
2022-12-17 07:59:42 +03:00
|
|
|
// root cmd. We need to perform some trickery to make it output help
|
|
|
|
// such that "sq help" and "sq --help" output the same thing.
|
|
|
|
slqCmd := newSLQCmd()
|
|
|
|
slqCmd.SetHelpFunc(func(command *cobra.Command, i []string) {
|
2022-12-18 07:31:06 +03:00
|
|
|
panicOn(rootCmd.Help())
|
2022-12-17 07:59:42 +03:00
|
|
|
})
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
addCmd(ru, rootCmd, slqCmd)
|
2021-02-22 10:37:00 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
addCmd(ru, rootCmd, newSrcAddCmd())
|
|
|
|
addCmd(ru, rootCmd, newSrcCommand())
|
|
|
|
addCmd(ru, rootCmd, newGroupCommand())
|
|
|
|
addCmd(ru, rootCmd, newListCmd())
|
|
|
|
addCmd(ru, rootCmd, newMoveCmd())
|
|
|
|
addCmd(ru, rootCmd, newRemoveCmd())
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
addCmd(ru, rootCmd, newInspectCmd())
|
|
|
|
addCmd(ru, rootCmd, newPingCmd())
|
|
|
|
addCmd(ru, rootCmd, newSQLCmd())
|
|
|
|
addCmd(ru, rootCmd, newScratchCmd())
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
tblCmd := addCmd(ru, rootCmd, newTblCmd())
|
|
|
|
addCmd(ru, tblCmd, newTblCopyCmd())
|
|
|
|
addCmd(ru, tblCmd, newTblTruncateCmd())
|
|
|
|
addCmd(ru, tblCmd, newTblDropCmd())
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2024-02-09 19:08:39 +03:00
|
|
|
dbCmd := addCmd(ru, rootCmd, newDBCmd())
|
|
|
|
addCmd(ru, dbCmd, newDBExecCmd())
|
|
|
|
dbDumpCmd := addCmd(ru, dbCmd, newDBDumpCmd())
|
|
|
|
addCmd(ru, dbDumpCmd, newDBDumpCatalogCmd())
|
|
|
|
addCmd(ru, dbDumpCmd, newDBDumpClusterCmd())
|
|
|
|
dbRestoreCmd := addCmd(ru, dbCmd, newDBRestoreCmd())
|
|
|
|
addCmd(ru, dbRestoreCmd, newDBRestoreCatalogCmd())
|
|
|
|
addCmd(ru, dbRestoreCmd, newDBRestoreClusterCmd())
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
addCmd(ru, rootCmd, newDiffCmd())
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
driverCmd := addCmd(ru, rootCmd, newDriverCmd())
|
|
|
|
addCmd(ru, driverCmd, newDriverListCmd())
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
configCmd := addCmd(ru, rootCmd, newConfigCmd())
|
|
|
|
addCmd(ru, configCmd, newConfigListCmd())
|
|
|
|
addCmd(ru, configCmd, newConfigGetCmd())
|
|
|
|
addCmd(ru, configCmd, newConfigSetCmd())
|
|
|
|
addCmd(ru, configCmd, newConfigLocationCmd())
|
|
|
|
addCmd(ru, configCmd, newConfigEditCmd())
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
cacheCmd := addCmd(ru, rootCmd, newCacheCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheLocationCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheStatCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheEnableCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheDisableCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheClearCmd())
|
|
|
|
addCmd(ru, cacheCmd, newCacheTreeCmd())
|
|
|
|
|
2024-01-31 09:04:10 +03:00
|
|
|
// addCmd(ru, rootCmd, newCompletionCmd())
|
2023-05-19 17:24:18 +03:00
|
|
|
addCmd(ru, rootCmd, newVersionCmd())
|
|
|
|
addCmd(ru, rootCmd, newManCmd())
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
xCmd := addCmd(ru, rootCmd, newXCmd())
|
|
|
|
addCmd(ru, xCmd, newXLockSrcCmd())
|
|
|
|
addCmd(ru, xCmd, newXLockConfigCmd())
|
|
|
|
addCmd(ru, xCmd, newXProgressCmd())
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
return rootCmd
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
// hasMatchingChildCommand returns true if s is a full or prefix
|
|
|
|
// match for any of cmd's children. For example, if cmd has
|
|
|
|
// children [inspect, ls, rm], then "insp" or "ls" would return true.
|
2024-01-31 09:04:10 +03:00
|
|
|
// Note that there's special handling for cobraz.ShellCompGenScriptsCmd.
|
2021-02-22 10:37:00 +03:00
|
|
|
func hasMatchingChildCommand(cmd *cobra.Command, s string) bool {
|
2024-01-31 09:04:10 +03:00
|
|
|
if strings.HasPrefix(cobraz.ShellCompGenScriptsCmd, s) { //nolint:gocritic // argOrder
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
for _, child := range cmd.Commands() {
|
|
|
|
if strings.HasPrefix(child.Name(), s) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
// addCmd adds the command returned by cmdFn to parentCmd.
|
2023-05-19 17:24:18 +03:00
|
|
|
func addCmd(ru *run.Run, parentCmd, cmd *cobra.Command) *cobra.Command {
|
2023-05-25 02:36:10 +03:00
|
|
|
cmd.DisableFlagsInUseLine = true
|
2021-02-22 10:37:00 +03:00
|
|
|
cmd.Flags().SortFlags = false
|
2023-05-22 18:08:14 +03:00
|
|
|
cmd.PersistentFlags().SortFlags = false
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
if cmd.Name() != "help" {
|
|
|
|
// Don't add the --help flag to the help command.
|
2023-04-19 08:28:09 +03:00
|
|
|
cmd.Flags().Bool(flag.Help, false, "help for "+cmd.Name())
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2022-12-30 05:02:10 +03:00
|
|
|
cmd.DisableAutoGenTag = true
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
|
2023-05-19 17:24:18 +03:00
|
|
|
ru.Cmd = cmd
|
|
|
|
ru.Args = args
|
|
|
|
return preRun(cmd, ru)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
runE := cmd.RunE
|
2020-08-06 20:58:47 +03:00
|
|
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
2023-04-19 08:28:09 +03:00
|
|
|
if cmd.Flags().Changed(flag.Version) {
|
2020-08-06 20:58:47 +03:00
|
|
|
// Bit of a hack: flag --version on any command
|
|
|
|
// results in execVersion being invoked
|
2021-02-22 10:37:00 +03:00
|
|
|
return execVersion(cmd, args)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2021-02-22 10:37:00 +03:00
|
|
|
return runE(cmd, args)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// We handle the errors ourselves (rather than let cobra do it)
|
|
|
|
cmd.SilenceErrors = true
|
|
|
|
cmd.SilenceUsage = true
|
|
|
|
|
|
|
|
parentCmd.AddCommand(cmd)
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|